Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
9b66f6c
[webview_flutter_wkwebview] Implement getCookies method to retrieve c…
khaled-0 Jan 20, 2026
4f2f4e8
[webview_flutter_android] Implement getCookies method
khaled-0 Jan 20, 2026
150d9cc
[webview_flutter_android] test for getCookies
khaled-0 Jan 21, 2026
b5f94a5
[webview_flutter_wkwebview] test for getCookies
khaled-0 Jan 21, 2026
2d5a916
[webview_flutter_wkwebview] match getAllCookies function signature
khaled-0 Jan 21, 2026
0cd5df3
[webview_flutter_wkwebview] Refactor getCookies to use if let
khaled-0 Jan 25, 2026
092db6e
[webview_flutter_android][webview_flutter_wkwebview] Use CookieManage…
khaled-0 Mar 30, 2026
45fa55f
[webview_flutter_android][webview_flutter_wkwebview] make api more co…
khaled-0 Feb 8, 2026
0f9fc43
[webview_flutter_android][webview_flutter_wkwebview] Bump webview_flu…
khaled-0 Mar 30, 2026
8fcd044
[webview_flutter_android][webview_flutter_wkwebview] Regenerate pigeo…
khaled-0 Mar 30, 2026
d60c7b1
[webview_flutter_wkwebview] Update getCookies test to use getAllCookies
khaled-0 Mar 30, 2026
3ec2eb1
[webview_flutter_android][webview_flutter_wkwebview] Apply formattings
khaled-0 Mar 30, 2026
5c25776
[webview_flutter_android] Remove getCookies_returnsEmptyStringIfNull
khaled-0 Mar 30, 2026
09a5802
[webview_flutter_android][webview_flutter_wkwebview] Bump version, ad…
khaled-0 Mar 30, 2026
2f01b98
[webview_flutter_wkwebview] Follow RFC 6265 guideline for `getCookies…
khaled-0 Mar 30, 2026
ab380f1
[webview_flutter_wkwebview][webview_flutter_android] Fail early in HT…
khaled-0 Apr 12, 2026
a7bc03c
Merge branch 'main' into implementations
khaled-0 Apr 14, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
## 4.11.0

* Adds support for retrieving cookies with `PlatformWebViewCookieManager.getCookies`.
* Adds support to opt out of Android inset changes. See
`AndroidWebViewController.setInsetsForWebContentToIgnore`.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1568,6 +1568,18 @@ abstract class PigeonApiCookieManager(
accept: Boolean
)

/**
* Gets all the cookies for the given URL.
*
* This may return multiple key-value pairs if multiple cookies are associated with this URL, in
* which case each cookie will be delimited by "; " characters (semicolon followed by a space).
* Each key-value pair will be of the form "key=value".
*
* Note: Any cookies set with the "Partitioned" attribute will only be returned for the top-level
* partition of url.
*/
abstract fun getCookies(pigeon_instance: android.webkit.CookieManager, url: String): String?

companion object {
@Suppress("LocalVariableName")
fun setUpMessageHandlers(binaryMessenger: BinaryMessenger, api: PigeonApiCookieManager?) {
Expand Down Expand Up @@ -1670,6 +1682,29 @@ abstract class PigeonApiCookieManager(
channel.setMessageHandler(null)
}
}
run {
val channel =
BasicMessageChannel<Any?>(
binaryMessenger,
"dev.flutter.pigeon.webview_flutter_android.CookieManager.getCookies",
codec)
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val pigeon_instanceArg = args[0] as android.webkit.CookieManager
val urlArg = args[1] as String
val wrapped: List<Any?> =
try {
listOf(api.getCookies(pigeon_instanceArg, urlArg))
} catch (exception: Throwable) {
AndroidWebkitLibraryPigeonUtils.wrapError(exception)
}
reply.reply(wrapped)
}
} else {
channel.setMessageHandler(null)
}
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,9 @@ public void setAcceptThirdPartyCookies(
@NonNull CookieManager pigeon_instance, @NonNull WebView webView, boolean accept) {
pigeon_instance.setAcceptThirdPartyCookies(webView, accept);
}

@Override
public @NonNull String getCookies(@NonNull CookieManager pigeon_instance, @NonNull String url) {
return pigeon_instance.getCookie(url);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.webkit.CookieManager;
import android.webkit.ValueCallback;
Expand Down Expand Up @@ -64,4 +65,20 @@ public void setAcceptThirdPartyCookies() {

verify(instance).setAcceptThirdPartyCookies(webView, accept);
}

@Test
public void getCookies_returnsCookieString() {
final PigeonApiCookieManager api = new TestProxyApiRegistrar().getPigeonApiCookieManager();

final CookieManager instance = mock(CookieManager.class);
final String domain = "https://flutter.dev";
final String cookieValue = "session=12345";

// Mock the CookieManager to return the cookie string
when(instance.getCookie(domain)).thenReturn(cookieValue);

final String result = api.getCookies(instance, domain);

assertEquals(cookieValue, result);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -496,9 +496,17 @@ class SampleMenu extends StatelessWidget {
}

Future<void> _onListCookies(BuildContext context) async {
final cookies =
await webViewController.runJavaScriptReturningResult('document.cookie')
as String;
final Uri? domain = Uri.tryParse(
(await webViewController.currentUrl()) ?? '',
);
final List<WebViewCookie> cookies;

if (domain == null) {
cookies = [];
} else {
cookies = await cookieManager.getCookies(domain);
}

if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
Expand Down Expand Up @@ -662,13 +670,12 @@ class SampleMenu extends StatelessWidget {
return webViewController.loadHtmlString(kAlertTestPage);
}

Widget _getCookieList(String cookies) {
if (cookies == '""') {
Widget _getCookieList(List<WebViewCookie> cookies) {
if (cookies.isEmpty) {
return Container();
}
final List<String> cookieList = cookies.split(';');
final Iterable<Text> cookieWidgets = cookieList.map(
(String cookie) => Text(cookie),
final Iterable<Text> cookieWidgets = cookies.map(
(WebViewCookie cookie) => Text(cookie.toString()),
);
return Column(
mainAxisAlignment: MainAxisAlignment.end,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1817,6 +1817,41 @@ class CookieManager extends PigeonInternalProxyApiBaseClass {
);
}

/// Gets all the cookies for the given URL.
///
/// This may return multiple key-value pairs if multiple cookies are associated with this URL,
/// in which case each cookie will be delimited by "; " characters (semicolon followed by a space).
/// Each key-value pair will be of the form "key=value".
///
/// Note: Any cookies set with the "Partitioned" attribute will only be returned for the top-level partition of url.
Future<String?> getCookies(String url) async {
final _PigeonInternalProxyApiBaseCodec pigeonChannelCodec =
_pigeonVar_codecCookieManager;
final BinaryMessenger? pigeonVar_binaryMessenger = pigeon_binaryMessenger;
const pigeonVar_channelName =
'dev.flutter.pigeon.webview_flutter_android.CookieManager.getCookies';
final pigeonVar_channel = BasicMessageChannel<Object?>(
pigeonVar_channelName,
pigeonChannelCodec,
binaryMessenger: pigeonVar_binaryMessenger,
);
final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(
<Object?>[this, url],
);
final pigeonVar_replyList = await pigeonVar_sendFuture as List<Object?>?;
if (pigeonVar_replyList == null) {
throw _createConnectionError(pigeonVar_channelName);
} else if (pigeonVar_replyList.length > 1) {
throw PlatformException(
code: pigeonVar_replyList[0]! as String,
message: pigeonVar_replyList[1] as String?,
details: pigeonVar_replyList[2],
);
} else {
return (pigeonVar_replyList[0] as String?);
}
}

@override
CookieManager pigeon_copy() {
return CookieManager.pigeon_detached(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,4 +87,26 @@ class AndroidWebViewCookieManager extends PlatformWebViewCookieManager {
.getInstanceWithWeakReference(controller.webViewIdentifier)!;
return _cookieManager.setAcceptThirdPartyCookies(webView, accept);
}

@override
Future<List<WebViewCookie>> getCookies(Uri url) async {
final String? cookies = await _cookieManager.getCookies(url.toString());
if (cookies == null || cookies.isEmpty) {
return [];
}

final webViewCookies = <WebViewCookie>[];
for (final String cookie in cookies.split('; ')) {
final List<String> cookieValue = cookie.split('=');
webViewCookies.add(
WebViewCookie(
name: cookieValue.first,
value: cookieValue.last,
domain: url.toString(),
),
);
Comment on lines +100 to +107
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The current cookie parsing logic is incorrect if a cookie's value contains an equals sign (=). Using cookie.split('=') and taking cookieValue.last will only capture the part of the value after the last =.

To correctly parse the cookie, you should take everything after the first = as the value.

Suggested change
final List<String> cookieValue = cookie.split('=');
webViewCookies.add(
WebViewCookie(
name: cookieValue.first,
value: cookieValue.last,
domain: url.toString(),
),
);
final List<String> cookieParts = cookie.split('=');
webViewCookies.add(
WebViewCookie(
name: cookieParts.first,
value: cookieParts.skip(1).join('='),
domain: url.toString(),
),
);

}

return webViewCookies;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,15 @@ abstract class CookieManager {

/// Sets whether the `WebView` should allow third party cookies to be set.
void setAcceptThirdPartyCookies(WebView webView, bool accept);

/// Gets all the cookies for the given URL.
///
/// This may return multiple key-value pairs if multiple cookies are associated with this URL,
/// in which case each cookie will be delimited by "; " characters (semicolon followed by a space).
/// Each key-value pair will be of the form "key=value".
///
/// Note: Any cookies set with the "Partitioned" attribute will only be returned for the top-level partition of url.
String? getCookies(String url);
}

/// A View that displays web pages.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ dependencies:
flutter:
sdk: flutter
meta: ^1.10.0
webview_flutter_platform_interface: ^2.14.0
webview_flutter_platform_interface: ^2.15.1

dev_dependencies:
build_runner: ^2.1.4
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,4 +108,93 @@ void main() {

verify(mockCookieManager.setAcceptThirdPartyCookies(webView, false));
});

test('getCookies should return list of WebViewCookie for a domain', () async {
final mockCookieManager = MockCookieManager();

// Mock the return value of getCookies
when(
mockCookieManager.getCookies('https://flutter.dev'),
).thenAnswer((_) => Future<String>.value('foo=bar; hello=world'));

final params =
AndroidWebViewCookieManagerCreationParams.fromPlatformWebViewCookieManagerCreationParams(
const PlatformWebViewCookieManagerCreationParams(),
);

final cookieManager = AndroidWebViewCookieManager(
params,
cookieManager: mockCookieManager,
);

final List<WebViewCookie> cookies = await cookieManager.getCookies(
Uri.parse('https://flutter.dev'),
);

expect(cookies.length, 2);

expect(cookies[0].name, 'foo');
expect(cookies[0].value, 'bar');
expect(cookies[0].domain, 'https://flutter.dev');

expect(cookies[1].name, 'hello');
expect(cookies[1].value, 'world');
expect(cookies[1].domain, 'https://flutter.dev');

verify(mockCookieManager.getCookies('https://flutter.dev')).called(1);
});

test('getCookies should return empty list if no cookies exist', () async {
final mockCookieManager = MockCookieManager();

when(
mockCookieManager.getCookies('https://flutter.dev'),
).thenAnswer((_) => Future<String>.value(''));

final params =
AndroidWebViewCookieManagerCreationParams.fromPlatformWebViewCookieManagerCreationParams(
const PlatformWebViewCookieManagerCreationParams(),
);

final cookieManager = AndroidWebViewCookieManager(
params,
cookieManager: mockCookieManager,
);

final List<WebViewCookie> cookies = await cookieManager.getCookies(
Uri.parse('https://flutter.dev'),
);

expect(cookies, isEmpty);
verify(mockCookieManager.getCookies('https://flutter.dev')).called(1);
});

test('getCookies should handle single cookie correctly', () async {
final mockCookieManager = MockCookieManager();

when(
mockCookieManager.getCookies('https://flutter.dev'),
).thenAnswer((_) => Future<String>.value('sessionId=abc123'));

final params =
AndroidWebViewCookieManagerCreationParams.fromPlatformWebViewCookieManagerCreationParams(
const PlatformWebViewCookieManagerCreationParams(),
);

final cookieManager = AndroidWebViewCookieManager(
params,
cookieManager: mockCookieManager,
);

final List<WebViewCookie> cookies = await cookieManager.getCookies(
Uri.parse('https://flutter.dev'),
);

expect(cookies.length, 1);
expect(cookies[0].name, 'sessionId');
expect(cookies[0].value, 'abc123');
expect(cookies[0].domain, 'https://flutter.dev');

verify(mockCookieManager.getCookies('https://flutter.dev')).called(1);
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,14 @@ class MockCookieManager extends _i1.Mock implements _i2.CookieManager {
)
as _i5.Future<void>);

@override
_i5.Future<String?> getCookies(String? url) =>
(super.noSuchMethod(
Invocation.method(#getCookies, [url]),
returnValue: _i5.Future<String?>.value(),
)
as _i5.Future<String?>);

@override
_i2.CookieManager pigeon_copy() =>
(super.noSuchMethod(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,14 @@ class MockCookieManager extends _i1.Mock implements _i2.CookieManager {
)
as _i3.Future<void>);

@override
_i3.Future<String?> getCookies(String? url) =>
(super.noSuchMethod(
Invocation.method(#getCookies, [url]),
returnValue: _i3.Future<String?>.value(),
)
as _i3.Future<String?>);

@override
_i2.CookieManager pigeon_copy() =>
(super.noSuchMethod(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
## 3.25.0

* Adds support for retrieving cookies with `PlatformWebViewCookieManager.getCookies`.
## 3.24.3

* Adds support to get failing url from DNS errors on iOS 26+.
Expand Down
Loading