Commit 77f09dd5 authored by Lorenzo Pichilli's avatar Lorenzo Pichilli

fixed iOS getCookies MyCookieManager, Added limited cookies support on iOS...

fixed iOS getCookies MyCookieManager, Added limited cookies support on iOS below 11.0 using JavaScript
parent 399602e5
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
<library name="Flutter Plugins"> <library name="Flutter Plugins">
<CLASSES> <CLASSES>
<root url="file://$PROJECT_DIR$" /> <root url="file://$PROJECT_DIR$" />
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/device_info-2.0.0-nullsafety.2" />
</CLASSES> </CLASSES>
<JAVADOC /> <JAVADOC />
<SOURCES /> <SOURCES />
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
- Added support for Dart null-safety feature - Added support for Dart null-safety feature
- Added Android Hybrid Composition support "Use PlatformViewLink widget for Android WebView" [#462](https://github.com/pichillilorenzo/flutter_inappwebview/pull/462) (thanks to [plateaukao](https://github.com/plateaukao) and [tneotia](https://github.com/tneotia)) - Added Android Hybrid Composition support "Use PlatformViewLink widget for Android WebView" [#462](https://github.com/pichillilorenzo/flutter_inappwebview/pull/462) (thanks to [plateaukao](https://github.com/plateaukao) and [tneotia](https://github.com/tneotia))
- Added `allowUniversalAccessFromFileURLs` and `allowFileAccessFromFileURLs` WebView options also for iOS (also thanks to [liranhao](https://github.com/liranhao)) - Added `allowUniversalAccessFromFileURLs` and `allowFileAccessFromFileURLs` WebView options also for iOS (also thanks to [liranhao](https://github.com/liranhao))
- Added limited cookies support on iOS below 11.0 using JavaScript
- Updated integration tests - Updated integration tests
- Merge "Upgraded appcompat to 1.2.0-rc-02" [#465](https://github.com/pichillilorenzo/flutter_inappwebview/pull/465) (thanks to [andreidiaconu](https://github.com/andreidiaconu)) - Merge "Upgraded appcompat to 1.2.0-rc-02" [#465](https://github.com/pichillilorenzo/flutter_inappwebview/pull/465) (thanks to [andreidiaconu](https://github.com/andreidiaconu))
- Merge "Added missing field 'headers' which returned by WebResourceResponse.toMap()" [#490](https://github.com/pichillilorenzo/flutter_inappwebview/pull/490) (thanks to [Doflatango](https://github.com/Doflatango)) - Merge "Added missing field 'headers' which returned by WebResourceResponse.toMap()" [#490](https://github.com/pichillilorenzo/flutter_inappwebview/pull/490) (thanks to [Doflatango](https://github.com/Doflatango))
...@@ -13,6 +14,7 @@ ...@@ -13,6 +14,7 @@
- Merge "iOS ChromeSafariBrowserManager - Fixing unnecessary casting of rootViewController to FlutterViewController" [#567](https://github.com/pichillilorenzo/flutter_inappwebview/pull/567) (thanks to [gunantosteven](https://github.com/gunantosteven)) - Merge "iOS ChromeSafariBrowserManager - Fixing unnecessary casting of rootViewController to FlutterViewController" [#567](https://github.com/pichillilorenzo/flutter_inappwebview/pull/567) (thanks to [gunantosteven](https://github.com/gunantosteven))
- Merge "Fix _channel.invokeMethod name for injectCSSFileFromUrl method" [#645](https://github.com/pichillilorenzo/flutter_inappwebview/pull/645) (thanks to [omralcrt](https://github.com/omralcrt)) - Merge "Fix _channel.invokeMethod name for injectCSSFileFromUrl method" [#645](https://github.com/pichillilorenzo/flutter_inappwebview/pull/645) (thanks to [omralcrt](https://github.com/omralcrt))
- Merge "Add android media intents on wildcard input accept" [#620](https://github.com/pichillilorenzo/flutter_inappwebview/pull/620) (thanks to [cbodin](https://github.com/cbodin)) - Merge "Add android media intents on wildcard input accept" [#620](https://github.com/pichillilorenzo/flutter_inappwebview/pull/620) (thanks to [cbodin](https://github.com/cbodin))
- Merge "Add ChromeSafariBrowser support for Android 11" [#538](https://github.com/pichillilorenzo/flutter_inappwebview/pull/538) (thanks to [DRSchlaubi](https://github.com/DRSchlaubi))
- Fixed missing properties initialization when using InAppWebViewController.fromInAppBrowser - Fixed missing properties initialization when using InAppWebViewController.fromInAppBrowser
- Fixed "Issue in Flutter web: 'Unsupported operation: Platform._operatingSystem'" [#507](https://github.com/pichillilorenzo/flutter_inappwebview/issues/507) - Fixed "Issue in Flutter web: 'Unsupported operation: Platform._operatingSystem'" [#507](https://github.com/pichillilorenzo/flutter_inappwebview/issues/507)
- Fixed "window.flutter_inappwebview.callHandler is not a function" [#218](https://github.com/pichillilorenzo/flutter_inappwebview/issues/218) - Fixed "window.flutter_inappwebview.callHandler is not a function" [#218](https://github.com/pichillilorenzo/flutter_inappwebview/issues/218)
......
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"flutter_downloader","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_downloader-1.5.2/","dependencies":[]},{"name":"flutter_inappwebview","path":"/Users/lorenzopichilli/Desktop/flutter_inappwebview/","dependencies":[]},{"name":"integration_test","path":"/Users/lorenzopichilli/flutter/.pub-cache/git/plugins-16f3281b04b0db12e609352b1c9544901392e428/packages/integration_test/","dependencies":[]},{"name":"path_provider","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider-1.6.27/","dependencies":[]},{"name":"permission_handler","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/permission_handler-5.0.1+1/","dependencies":[]},{"name":"url_launcher","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher-6.0.0-nullsafety.4/","dependencies":[]}],"android":[{"name":"flutter_downloader","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_downloader-1.5.2/","dependencies":[]},{"name":"flutter_inappwebview","path":"/Users/lorenzopichilli/Desktop/flutter_inappwebview/","dependencies":[]},{"name":"integration_test","path":"/Users/lorenzopichilli/flutter/.pub-cache/git/plugins-16f3281b04b0db12e609352b1c9544901392e428/packages/integration_test/","dependencies":[]},{"name":"path_provider","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider-1.6.27/","dependencies":[]},{"name":"permission_handler","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/permission_handler-5.0.1+1/","dependencies":[]},{"name":"url_launcher","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher-6.0.0-nullsafety.4/","dependencies":[]}],"macos":[{"name":"path_provider_macos","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_macos-0.0.4+8/","dependencies":[]},{"name":"url_launcher_macos","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_macos-0.1.0-nullsafety.2/","dependencies":[]}],"linux":[{"name":"path_provider_linux","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_linux-0.0.1+2/","dependencies":[]},{"name":"url_launcher_linux","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_linux-0.1.0-nullsafety.3/","dependencies":[]}],"windows":[{"name":"path_provider_windows","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_windows-0.0.4+3/","dependencies":[]},{"name":"url_launcher_windows","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_windows-0.1.0-nullsafety.2/","dependencies":[]}],"web":[]},"dependencyGraph":[{"name":"flutter_downloader","dependencies":[]},{"name":"flutter_inappwebview","dependencies":[]},{"name":"integration_test","dependencies":[]},{"name":"path_provider","dependencies":["path_provider_macos","path_provider_linux","path_provider_windows"]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_macos","dependencies":[]},{"name":"path_provider_windows","dependencies":[]},{"name":"permission_handler","dependencies":[]},{"name":"url_launcher","dependencies":["url_launcher_linux","url_launcher_macos","url_launcher_windows"]},{"name":"url_launcher_linux","dependencies":[]},{"name":"url_launcher_macos","dependencies":[]},{"name":"url_launcher_windows","dependencies":[]}],"date_created":"2021-01-29 00:53:56.206541","version":"1.26.0-13.0.pre.194"} {"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"device_info","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/device_info-2.0.0-nullsafety.2/","dependencies":[]},{"name":"flutter_downloader","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_downloader-1.5.2/","dependencies":[]},{"name":"flutter_inappwebview","path":"/Users/lorenzopichilli/Desktop/flutter_inappwebview/","dependencies":["device_info"]},{"name":"integration_test","path":"/Users/lorenzopichilli/flutter/.pub-cache/git/plugins-16f3281b04b0db12e609352b1c9544901392e428/packages/integration_test/","dependencies":[]},{"name":"path_provider","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider-1.6.27/","dependencies":[]},{"name":"permission_handler","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/permission_handler-5.0.1+1/","dependencies":[]},{"name":"url_launcher","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher-6.0.0-nullsafety.4/","dependencies":[]}],"android":[{"name":"device_info","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/device_info-2.0.0-nullsafety.2/","dependencies":[]},{"name":"flutter_downloader","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_downloader-1.5.2/","dependencies":[]},{"name":"flutter_inappwebview","path":"/Users/lorenzopichilli/Desktop/flutter_inappwebview/","dependencies":["device_info"]},{"name":"integration_test","path":"/Users/lorenzopichilli/flutter/.pub-cache/git/plugins-16f3281b04b0db12e609352b1c9544901392e428/packages/integration_test/","dependencies":[]},{"name":"path_provider","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider-1.6.27/","dependencies":[]},{"name":"permission_handler","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/permission_handler-5.0.1+1/","dependencies":[]},{"name":"url_launcher","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher-6.0.0-nullsafety.4/","dependencies":[]}],"macos":[{"name":"path_provider_macos","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_macos-0.0.4+8/","dependencies":[]},{"name":"url_launcher_macos","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_macos-0.1.0-nullsafety.2/","dependencies":[]}],"linux":[{"name":"path_provider_linux","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_linux-0.0.1+2/","dependencies":[]},{"name":"url_launcher_linux","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_linux-0.1.0-nullsafety.3/","dependencies":[]}],"windows":[{"name":"path_provider_windows","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_windows-0.0.4+3/","dependencies":[]},{"name":"url_launcher_windows","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_windows-0.1.0-nullsafety.2/","dependencies":[]}],"web":[]},"dependencyGraph":[{"name":"device_info","dependencies":[]},{"name":"flutter_downloader","dependencies":[]},{"name":"flutter_inappwebview","dependencies":["device_info"]},{"name":"integration_test","dependencies":[]},{"name":"path_provider","dependencies":["path_provider_macos","path_provider_linux","path_provider_windows"]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_macos","dependencies":[]},{"name":"path_provider_windows","dependencies":[]},{"name":"permission_handler","dependencies":[]},{"name":"url_launcher","dependencies":["url_launcher_linux","url_launcher_macos","url_launcher_windows"]},{"name":"url_launcher_linux","dependencies":[]},{"name":"url_launcher_macos","dependencies":[]},{"name":"url_launcher_windows","dependencies":[]}],"date_created":"2021-01-30 14:49:49.654803","version":"1.26.0-18.0.pre.90"}
\ No newline at end of file \ No newline at end of file
#
# NOTE: This podspec is NOT to be published. It is only used as a local source!
# This is a generated file; do not edit or check into version control.
#
Pod::Spec.new do |s|
s.name = 'Flutter'
s.version = '1.0.0'
s.summary = 'High-performance, high-fidelity mobile apps.'
s.homepage = 'https://flutter.io'
s.license = { :type => 'MIT' }
s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' }
s.source = { :git => 'https://github.com/flutter/engine', :tag => s.version.to_s }
s.ios.deployment_target = '8.0'
# Framework linking is handled by Flutter tooling, not CocoaPods.
# Add a placeholder to satisfy `s.dependency 'Flutter'` plugin podspecs.
s.vendored_frameworks = 'path/to/nothing'
end
...@@ -254,6 +254,7 @@ ...@@ -254,6 +254,7 @@
); );
inputPaths = ( inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh",
"${BUILT_PRODUCTS_DIR}/device_info/device_info.framework",
"${BUILT_PRODUCTS_DIR}/flutter_downloader/flutter_downloader.framework", "${BUILT_PRODUCTS_DIR}/flutter_downloader/flutter_downloader.framework",
"${BUILT_PRODUCTS_DIR}/flutter_inappwebview/flutter_inappwebview.framework", "${BUILT_PRODUCTS_DIR}/flutter_inappwebview/flutter_inappwebview.framework",
"${BUILT_PRODUCTS_DIR}/integration_test/integration_test.framework", "${BUILT_PRODUCTS_DIR}/integration_test/integration_test.framework",
...@@ -262,6 +263,7 @@ ...@@ -262,6 +263,7 @@
); );
name = "[CP] Embed Pods Frameworks"; name = "[CP] Embed Pods Frameworks";
outputPaths = ( outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/device_info.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_downloader.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_downloader.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_inappwebview.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_inappwebview.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/integration_test.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/integration_test.framework",
......
...@@ -146,7 +146,7 @@ class MyCookieManager: NSObject, FlutterPlugin { ...@@ -146,7 +146,7 @@ class MyCookieManager: NSObject, FlutterPlugin {
if let urlHost = URL(string: url)?.host { if let urlHost = URL(string: url)?.host {
MyCookieManager.httpCookieStore!.getAllCookies { (cookies) in MyCookieManager.httpCookieStore!.getAllCookies { (cookies) in
for cookie in cookies { for cookie in cookies {
if urlHost.hasSuffix(cookie.domain) { if urlHost.hasSuffix(cookie.domain) || cookie.domain.hasSuffix(urlHost) {
var sameSite: String? = nil var sameSite: String? = nil
if #available(iOS 13.0, *) { if #available(iOS 13.0, *) {
if let sameSiteValue = cookie.sameSitePolicy?.rawValue { if let sameSiteValue = cookie.sameSitePolicy?.rawValue {
......
import 'dart:async'; import 'dart:async';
import 'dart:io';
import 'package:device_info/device_info.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:intl/intl.dart';
import 'in_app_webview_controller.dart';
import 'headless_in_app_webview.dart';
import 'types.dart'; import 'types.dart';
///Class that implements a singleton object (shared instance) which manages the cookies used by WebView instances. ///Class that implements a singleton object (shared instance) which manages the cookies used by WebView instances.
///On Android, it is implemented using [CookieManager](https://developer.android.com/reference/android/webkit/CookieManager). ///On Android, it is implemented using [CookieManager](https://developer.android.com/reference/android/webkit/CookieManager).
///On iOS, it is implemented using [WKHTTPCookieStore](https://developer.apple.com/documentation/webkit/wkhttpcookiestore). ///On iOS, it is implemented using [WKHTTPCookieStore](https://developer.apple.com/documentation/webkit/wkhttpcookiestore).
/// ///
///**NOTE for iOS**: available from iOS 11.0+. ///**NOTE for iOS below 11.0 (LIMITED SUPPORT!)**: in this case, almost all of the methods ([CookieManager.deleteAllCookies] is not supported!)
///has been implemented using JavaScript because there is no other way to work with them on iOS below 11.0.
///See https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#restrict_access_to_cookies for JavaScript restrictions.
class CookieManager { class CookieManager {
static CookieManager? _instance; static CookieManager? _instance;
static const MethodChannel _channel = const MethodChannel( static const MethodChannel _channel = const MethodChannel(
...@@ -27,10 +34,17 @@ class CookieManager { ...@@ -27,10 +34,17 @@ class CookieManager {
static Future<dynamic> _handleMethod(MethodCall call) async {} static Future<dynamic> _handleMethod(MethodCall call) async {}
///Sets a cookie for the given [url]. Any existing cookie with the same [host], [path] and [name] will be replaced with the new cookie. The cookie being set will be ignored if it is expired. ///Sets a cookie for the given [url]. Any existing cookie with the same [host], [path] and [name] will be replaced with the new cookie.
///The cookie being set will be ignored if it is expired.
/// ///
///The default value of [path] is `"/"`. ///The default value of [path] is `"/"`.
///If [domain] is `null`, its default value will be the domain name of [url]. ///If [domain] is `null`, its default value will be the domain name of [url].
///
///[iosBelow11WebViewController] could be used if you need to set a session-only cookie using JavaScript (so [isHttpOnly] cannot be set, see: https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#restrict_access_to_cookies)
///on the current URL of the [WebView] managed by that controller when you need to target iOS below 11. In this case the [url] parameter is ignored.
///
///**NOTE for iOS below 11.0**: If [iosBelow11WebViewController] is `null` or JavaScript is disabled for it, it will try to use a [HeadlessInAppWebView]
///to set the cookie (session-only cookie won't work! In that case, you should set also [expiresDate] or [maxAge]).
Future<void> setCookie( Future<void> setCookie(
{required String url, {required String url,
required String name, required String name,
...@@ -41,7 +55,8 @@ class CookieManager { ...@@ -41,7 +55,8 @@ class CookieManager {
int? maxAge, int? maxAge,
bool? isSecure, bool? isSecure,
bool? isHttpOnly, bool? isHttpOnly,
HTTPCookieSameSitePolicy? sameSite}) async { HTTPCookieSameSitePolicy? sameSite,
InAppWebViewController? iosBelow11WebViewController}) async {
if (domain == null) domain = _getDomainName(url); if (domain == null) domain = _getDomainName(url);
assert(url.isNotEmpty); assert(url.isNotEmpty);
...@@ -50,6 +65,56 @@ class CookieManager { ...@@ -50,6 +65,56 @@ class CookieManager {
assert(domain.isNotEmpty); assert(domain.isNotEmpty);
assert(path.isNotEmpty); assert(path.isNotEmpty);
if (Platform.isIOS) {
DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
IosDeviceInfo iosInfo = await deviceInfo.iosInfo;
var version = double.tryParse(iosInfo.systemVersion);
if (version != null && version < 11.0) {
var cookieValue = name + "=" + value + "; Domain=" + domain + "; Path=" + path;
if (expiresDate != null)
cookieValue += "; Expires=" + _getCookieExpirationDate(expiresDate);
if (maxAge != null)
cookieValue += "; Max-Age=" + maxAge.toString();
if (isSecure != null && isSecure)
cookieValue += "; Secure";
if (sameSite != null)
cookieValue += "; SameSite=" + sameSite.toValue();
cookieValue += ";";
if (iosBelow11WebViewController != null) {
InAppWebViewGroupOptions? options = await iosBelow11WebViewController.getOptions();
if (options != null && options.crossPlatform != null &&
options.crossPlatform!.javaScriptEnabled == true) {
await iosBelow11WebViewController.evaluateJavascript(
source: 'document.cookie="$cookieValue"');
return;
}
}
var setCookieCompleter = Completer<void>();
var headlessWebView = new HeadlessInAppWebView(
initialUrl: url,
onLoadStop: (controller, url) async {
await controller.evaluateJavascript(
source: 'document.cookie="$cookieValue"');
setCookieCompleter.complete();
},
);
await headlessWebView.run();
await setCookieCompleter.future;
await headlessWebView.dispose();
return;
}
}
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('url', () => url); args.putIfAbsent('url', () => url);
args.putIfAbsent('name', () => name); args.putIfAbsent('name', () => name);
...@@ -66,38 +131,157 @@ class CookieManager { ...@@ -66,38 +131,157 @@ class CookieManager {
} }
///Gets all the cookies for the given [url]. ///Gets all the cookies for the given [url].
Future<List<Cookie>> getCookies({required String url}) async { ///
///[iosBelow11WebViewController] is used for getting the cookies (also session-only cookies) using JavaScript (cookies with `isHttpOnly` enabled cannot be found, see: https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#restrict_access_to_cookies)
///from the current context of the [WebView] managed by that controller when you need to target iOS below 11. JavaScript must be enabled in order to work.
///In this case the [url] parameter is ignored.
///
///**NOTE for iOS below 11.0**: All the cookies returned this way will have all the properties to `null` except for [Cookie.name] and [Cookie.value].
///If [iosBelow11WebViewController] is `null` or JavaScript is disabled for it, it will try to use a [HeadlessInAppWebView]
///to get the cookies (session-only cookies and cookies with `isHttpOnly` enabled won't be found!).
Future<List<Cookie>> getCookies({required String url, InAppWebViewController? iosBelow11WebViewController}) async {
assert(url.isNotEmpty); assert(url.isNotEmpty);
List<Cookie> cookies = [];
if (Platform.isIOS) {
DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
IosDeviceInfo iosInfo = await deviceInfo.iosInfo;
var version = double.tryParse(iosInfo.systemVersion);
if (version != null && version < 11.0) {
if (iosBelow11WebViewController != null) {
InAppWebViewGroupOptions? options = await iosBelow11WebViewController.getOptions();
if (options != null && options.crossPlatform != null &&
options.crossPlatform!.javaScriptEnabled == true) {
List<String> documentCookies = (await iosBelow11WebViewController.evaluateJavascript(source: 'document.cookie') as String)
.split(';').map((documentCookie) => documentCookie.trim()).toList();
documentCookies.forEach((documentCookie) {
List<String> cookie = documentCookie.split('=');
cookies.add(Cookie(
name: cookie[0],
value: cookie[1],
)
);
});
return cookies;
}
}
var pageLoaded = Completer<void>();
var headlessWebView = new HeadlessInAppWebView(
initialUrl: url,
onLoadStop: (controller, url) async {
pageLoaded.complete();
},
);
await headlessWebView.run();
await pageLoaded.future;
List<String> documentCookies = (await headlessWebView.webViewController.evaluateJavascript(source: 'document.cookie') as String)
.split(';').map((documentCookie) => documentCookie.trim()).toList();
documentCookies.forEach((documentCookie) {
List<String> cookie = documentCookie.split('=');
cookies.add(Cookie(
name: cookie[0],
value: cookie[1],
)
);
});
await headlessWebView.dispose();
return cookies;
}
}
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('url', () => url); args.putIfAbsent('url', () => url);
List<dynamic> cookieListMap = List<dynamic> cookieListMap =
await _channel.invokeMethod('getCookies', args); await _channel.invokeMethod('getCookies', args);
cookieListMap = cookieListMap.cast<Map<dynamic, dynamic>>(); cookieListMap = cookieListMap.cast<Map<dynamic, dynamic>>();
List<Cookie> cookies = []; cookieListMap.forEach((cookieMap) {
for (var i = 0; i < cookieListMap.length; i++) {
cookies.add(Cookie( cookies.add(Cookie(
name: cookieListMap[i]["name"], name: cookieMap["name"],
value: cookieListMap[i]["value"], value: cookieMap["value"],
expiresDate: cookieListMap[i]["expiresDate"], expiresDate: cookieMap["expiresDate"],
isSessionOnly: cookieListMap[i]["isSessionOnly"], isSessionOnly: cookieMap["isSessionOnly"],
domain: cookieListMap[i]["domain"], domain: cookieMap["domain"],
sameSite: sameSite:
HTTPCookieSameSitePolicy.fromValue(cookieListMap[i]["sameSite"]), HTTPCookieSameSitePolicy.fromValue(cookieMap["sameSite"]),
isSecure: cookieListMap[i]["isSecure"], isSecure: cookieMap["isSecure"],
isHttpOnly: cookieListMap[i]["isHttpOnly"], isHttpOnly: cookieMap["isHttpOnly"],
path: cookieListMap[i]["path"])); path: cookieMap["path"]));
} });
return cookies; return cookies;
} }
///Gets a cookie by its [name] for the given [url]. ///Gets a cookie by its [name] for the given [url].
///
///[iosBelow11WebViewController] is used for getting the cookie (also session-only cookie) using JavaScript (cookie with `isHttpOnly` enabled cannot be found, see: https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#restrict_access_to_cookies)
///from the current context of the [WebView] managed by that controller when you need to target iOS below 11. JavaScript must be enabled in order to work.
///In this case the [url] parameter is ignored.
///
///**NOTE for iOS below 11.0**: All the cookies returned this way will have all the properties to `null` except for [Cookie.name] and [Cookie.value].
///If [iosBelow11WebViewController] is `null` or JavaScript is disabled for it, it will try to use a [HeadlessInAppWebView]
///to get the cookie (session-only cookie and cookie with `isHttpOnly` enabled won't be found!).
Future<Cookie?> getCookie( Future<Cookie?> getCookie(
{required String url, required String name}) async { {required String url, required String name, InAppWebViewController? iosBelow11WebViewController}) async {
assert(url.isNotEmpty); assert(url.isNotEmpty);
assert(name.isNotEmpty); assert(name.isNotEmpty);
if (Platform.isIOS) {
DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
IosDeviceInfo iosInfo = await deviceInfo.iosInfo;
var version = double.tryParse(iosInfo.systemVersion);
if (version != null && version < 11.0) {
if (iosBelow11WebViewController != null) {
InAppWebViewGroupOptions? options = await iosBelow11WebViewController.getOptions();
if (options != null && options.crossPlatform != null &&
options.crossPlatform!.javaScriptEnabled == true) {
List<String> documentCookies = (await iosBelow11WebViewController.evaluateJavascript(source: 'document.cookie') as String)
.split(';').map((documentCookie) => documentCookie.trim()).toList();
for (var i = 0; i < documentCookies.length; i++) {
List<String> cookie = documentCookies[i].split('=');
if (cookie[0] == name)
return Cookie(
name: cookie[0],
value: cookie[1]);
}
return null;
}
}
var pageLoaded = Completer<void>();
var headlessWebView = new HeadlessInAppWebView(
initialUrl: url,
onLoadStop: (controller, url) async {
pageLoaded.complete();
},
);
await headlessWebView.run();
await pageLoaded.future;
List<String> documentCookies = (await headlessWebView.webViewController.evaluateJavascript(source: 'document.cookie') as String)
.split(';').map((documentCookie) => documentCookie.trim()).toList();
await headlessWebView.dispose();
for (var i = 0; i < documentCookies.length; i++) {
List<String> cookie = documentCookies[i].split('=');
if (cookie[0] == name)
return Cookie(
name: cookie[0],
value: cookie[1]);
}
return null;
}
}
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('url', () => url); args.putIfAbsent('url', () => url);
List<dynamic> cookies = await _channel.invokeMethod('getCookies', args); List<dynamic> cookies = await _channel.invokeMethod('getCookies', args);
...@@ -123,18 +307,34 @@ class CookieManager { ...@@ -123,18 +307,34 @@ class CookieManager {
///Removes a cookie by its [name] for the given [url], [domain] and [path]. ///Removes a cookie by its [name] for the given [url], [domain] and [path].
/// ///
///The default value of [path] is `"/"`. ///The default value of [path] is `"/"`.
///If [domain] is `null` or empty, its default value will be the domain name of [url]. ///If [domain] is empty, its default value will be the domain name of [url].
///
///[iosBelow11WebViewController] is used for deleting the cookie (also session-only cookie) using JavaScript (cookie with `isHttpOnly` enabled cannot be deleted, see: https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#restrict_access_to_cookies)
///from the current context of the [WebView] managed by that controller when you need to target iOS below 11. JavaScript must be enabled in order to work.
///In this case the [url] parameter is ignored.
///
///**NOTE for iOS below 11.0**: If [iosBelow11WebViewController] is `null` or JavaScript is disabled for it, it will try to use a [HeadlessInAppWebView]
///to delete the cookie (session-only cookie and cookie with `isHttpOnly` enabled won't be deleted!).
Future<void> deleteCookie( Future<void> deleteCookie(
{required String url, {required String url,
required String name, required String name,
String domain = "", String domain = "",
String path = "/"}) async { String path = "/",
InAppWebViewController? iosBelow11WebViewController}) async {
if (domain.isEmpty) domain = _getDomainName(url); if (domain.isEmpty) domain = _getDomainName(url);
assert(url.isNotEmpty); assert(url.isNotEmpty);
assert(name.isNotEmpty); assert(name.isNotEmpty);
assert(url.isNotEmpty);
assert(url.isNotEmpty); if (Platform.isIOS) {
DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
IosDeviceInfo iosInfo = await deviceInfo.iosInfo;
var version = double.tryParse(iosInfo.systemVersion);
if (version != null && version < 11.0) {
await setCookie(url: url, name: name, value: "", path: path, domain: domain, maxAge: -1, iosBelow11WebViewController: iosBelow11WebViewController);
return;
}
}
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('url', () => url); args.putIfAbsent('url', () => url);
...@@ -147,15 +347,34 @@ class CookieManager { ...@@ -147,15 +347,34 @@ class CookieManager {
///Removes all cookies for the given [url], [domain] and [path]. ///Removes all cookies for the given [url], [domain] and [path].
/// ///
///The default value of [path] is `"/"`. ///The default value of [path] is `"/"`.
///If [domain] is `null` or empty, its default value will be the domain name of [url]. ///If [domain] is empty, its default value will be the domain name of [url].
///
///[iosBelow11WebViewController] is used for deleting the cookies (also session-only cookies) using JavaScript (cookies with `isHttpOnly` enabled cannot be deleted, see: https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#restrict_access_to_cookies)
///from the current context of the [WebView] managed by that controller when you need to target iOS below 11. JavaScript must be enabled in order to work.
///In this case the [url] parameter is ignored.
///
///**NOTE for iOS below 11.0**: If [iosBelow11WebViewController] is `null` or JavaScript is disabled for it, it will try to use a [HeadlessInAppWebView]
///to delete the cookies (session-only cookies and cookies with `isHttpOnly` enabled won't be deleted!).
Future<void> deleteCookies( Future<void> deleteCookies(
{required String url, String domain = "", String path = "/"}) async { {required String url, String domain = "", String path = "/",
InAppWebViewController? iosBelow11WebViewController}) async {
if (domain.isEmpty) domain = _getDomainName(url); if (domain.isEmpty) domain = _getDomainName(url);
assert(url.isNotEmpty);
assert(url.isNotEmpty);
assert(url.isNotEmpty); assert(url.isNotEmpty);
if (Platform.isIOS) {
DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
IosDeviceInfo iosInfo = await deviceInfo.iosInfo;
var version = double.tryParse(iosInfo.systemVersion);
if (version != null && version < 11.0) {
List<Cookie> cookies = await getCookies(url: url, iosBelow11WebViewController: iosBelow11WebViewController);
for (var i = 0; i < cookies.length; i++) {
await setCookie(url: url, name: cookies[i].name, value: "", path: path, domain: domain, maxAge: -1, iosBelow11WebViewController: iosBelow11WebViewController);
}
return;
}
}
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('url', () => url); args.putIfAbsent('url', () => url);
args.putIfAbsent('domain', () => domain); args.putIfAbsent('domain', () => domain);
...@@ -164,6 +383,8 @@ class CookieManager { ...@@ -164,6 +383,8 @@ class CookieManager {
} }
///Removes all cookies. ///Removes all cookies.
///
///**NOTE for iOS**: available from iOS 11.0+.
Future<void> deleteAllCookies() async { Future<void> deleteAllCookies() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
await _channel.invokeMethod('deleteAllCookies', args); await _channel.invokeMethod('deleteAllCookies', args);
...@@ -176,4 +397,9 @@ class CookieManager { ...@@ -176,4 +397,9 @@ class CookieManager {
if (domain == null) return ""; if (domain == null) return "";
return domain.startsWith("www.") ? domain.substring(4) : domain; return domain.startsWith("www.") ? domain.substring(4) : domain;
} }
String _getCookieExpirationDate(int expiresDate) {
var dateTime = DateTime.fromMillisecondsSinceEpoch(expiresDate).toUtc();
return DateFormat('EEE, d MMM yyyy hh:mm:ss', "en_US").format(dateTime) + ' GMT';
}
} }
...@@ -12,6 +12,8 @@ dependencies: ...@@ -12,6 +12,8 @@ dependencies:
sdk: flutter sdk: flutter
uuid: ^3.0.0-nullsafety.0 uuid: ^3.0.0-nullsafety.0
mime: ^1.0.0-nullsafety.0 mime: ^1.0.0-nullsafety.0
intl: ^0.17.0-nullsafety.2
device_info: ^2.0.0-nullsafety.2
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment