-
Notifications
You must be signed in to change notification settings - Fork 3.6k
[webview_flutter] Implement getCookies #10833
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Caveats: Domain name can not be null
|
It looks like this pull request may not have tests. Please make sure to add tests or get an explicit test exemption before merging. If you are not sure if you need tests, consider this rule of thumb: the purpose of a test is to make sure someone doesn't accidentally revert the fix. Ask yourself, is there anything in your PR that you feel it is important we not accidentally revert back to how it was before your fix? Reviewers: Read the Tree Hygiene page and make sure this patch meets those guidelines before LGTMing. If you believe this PR qualifies for a test exemption, contact "@test-exemption-reviewer" in the #hackers channel in Discord (don't just cc them here, they won't see it!). The test exemption team is a small volunteer group, so all reviewers should feel empowered to ask for tests, without delegating that responsibility entirely to the test exemption group. |
|
I don't know how I should go about writing test for this change, so I'm looking for suggestions. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code Review
This pull request introduces the getCookies method to WebViewCookieManager, enabling retrieval of cookies from webviews on both Android and iOS/macOS. The implementation is backed by native platform APIs. My review identifies a few critical issues in the platform-specific implementations. On Android, the cookie string parsing is not robust and can fail if cookie values contain certain characters. On the WebKit (iOS/macOS) side, there's an unsafe access to cookie properties that could lead to creating invalid WebViewCookie objects. I've also suggested a small improvement to the WebViewCookie.toString() method for better debuggability.
| final List<WebViewCookie> webViewCookies = List.of([]); | ||
| for (final String cookie in cookies.split('; ')) { | ||
| final List<String> cookieValue = cookie.split('='); | ||
| webViewCookies.add( | ||
| WebViewCookie( | ||
| name: cookieValue.first, | ||
| value: cookieValue.last, | ||
| domain: domain.toString(), | ||
| ), | ||
| ); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The current cookie parsing logic using cookie.split('=') and accessing .first and .last is not robust. If a cookie's value contains an = character, the value will be truncated, and only the part after the last = will be used. This leads to incorrect cookie values.
A safer approach is to find the first occurrence of = to separate the name and value. Also, initializing the list with <WebViewCookie>[] is more idiomatic than List.of([]).
final List<WebViewCookie> webViewCookies = <WebViewCookie>[];
for (final String cookie in cookies.split('; ')) {
final int separatorIndex = cookie.indexOf('=');
if (separatorIndex == -1) {
continue;
}
webViewCookies.add(
WebViewCookie(
name: cookie.substring(0, separatorIndex),
value: cookie.substring(separatorIndex + 1),
domain: domain.toString(),
),
);
}There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It won't, because cookies are URL encoded
| return WebViewCookie( | ||
| name: props[HttpCookiePropertyKey.name].toString(), | ||
| value: props[HttpCookiePropertyKey.value].toString(), | ||
| domain: props[HttpCookiePropertyKey.domain].toString(), | ||
| path: props[HttpCookiePropertyKey.path].toString(), | ||
| ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The current implementation for creating a WebViewCookie from properties is unsafe. It uses .toString() on map values that can be null. If a property like name, value, or domain is missing from the props map, this will result in a WebViewCookie with fields containing the string 'null', which is incorrect. For path, it should fall back to the default value '/' if it's missing.
For required fields like name, value, and domain, if they are missing, the cookie is invalid and should be skipped.
final Object? name = props[HttpCookiePropertyKey.name];
final Object? value = props[HttpCookiePropertyKey.value];
final Object? domain = props[HttpCookiePropertyKey.domain];
if (name == null || value == null || domain == null) {
return null;
}
return WebViewCookie(
name: name.toString(),
value: value.toString(),
domain: domain.toString(),
path: props[HttpCookiePropertyKey.path]?.toString() ?? '/',
);|
|
||
| @override | ||
| String toString() { | ||
| return 'WebViewCookie{name: $name, domain: $domain, path: $path}'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
toString more often than not, is for debugging only (sometimes the compiler will optimize this override out). I wonder what is WebViewCookie toString for (is it still debug-only?) and why value shouldn't be included?
Please follow these instructions so that CI can run.
What specifically are you looking for in terms of guidance here? Have you looked at the existing tests (native and Dart) for these packages? We have significant test coverage, so there should be clear examples to follow. |
swift-format reformatted the entire HTTPCookieStoreProxyAPITests.swift
GCC_TREAT_WARNINGS_AS_ERRORS = YES (no-shit)
|
@stuartmorgan-g all relevant tests are passing now |
|
Adding to the review triage queues. The tool-created changes to |
| /// This is a no op on iOS versions below 11. | ||
| Future<void> setCookie(WebViewCookie cookie) => platform.setCookie(cookie); | ||
|
|
||
| /// Gets a list of existing cookie for all or specified domain for all WebViews. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does "all WebViews" mean? All webviews in the app or in the device?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in the app. should be obvious. I used existing comments as a reference
|
|
||
| @override | ||
| String toString() { | ||
| return 'WebViewCookie{name: $name, domain: $domain, path: $path}'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
toString more often than not, is for debugging only (sometimes the compiler will optimize this override out). I wonder what is WebViewCookie toString for (is it still debug-only?) and why value shouldn't be included?
| } | ||
|
|
||
| /// Gets a list of existing cookie for all or specified domain for all WebViews. | ||
| /// Android: Entire domain must be provided alongside scheme |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: this needs a new line after the first paragraph.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same question regarding all WebViews. Also, something like sounds a bit more explicit about how to get cookies for all domains:
Gets a list of existing cookies for all
WebViews for the specified domain, or for all domains ifdomainis not specified.
|
|
||
| /// Gets a list of existing cookie for all or specified domain for all WebViews. | ||
| /// Android: Entire domain must be provided alongside scheme | ||
| /// iOS: ignores scheme and supports partial match based on host |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: missing periods at the end of both sentences (here and everywhere). Also macOS?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
iOS and MacOS, I'll update the comments
| domain: String?, | ||
| completion: @escaping (Result<[HTTPCookie], any Error>) -> Void | ||
| ) { | ||
| if domain == nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
uber nit: could you get rid of the ! by using if let / guard let?
| } | ||
|
|
||
| pigeonInstance.getAllCookies { cookies in | ||
| completion(.success(cookies.filter { $0.domain.contains(domain!) })) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if we're doing the filtering ourselves, then why do we have the limitation that Android can only do full domain matching (And also does this have to be part of the API? since the caller can do that themselves too)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we're only doing the filtering on ios/macos side. (to match behavior on both platform)
Android has no way of getting cookies for all domain so passing a specific domain is necessary.
| await webViewController.runJavaScriptReturningResult('document.cookie') | ||
| as String; | ||
| final Uri? domain = Uri.tryParse( | ||
| (await webViewController.currentUrl()) ?? '', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm this changes the behavior of the sample app I think? If currentUrl is null before the change the page would be empty right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
well yes kinda, it would print all the cookies for all domain if currentUrl is null for ios/macos
This pr adds a way to retrive cookies from webview by using native platform api provided by
CookieManagerandWKHTTPCookieStoreAddresses flutter/flutter#27795
Pre-Review Checklist
[shared_preferences]pubspec.yamlwith an appropriate new version according to the pub versioning philosophy, or I have commented below to indicate which version change exemption this PR falls under1.CHANGELOG.mdto add a description of the change, following repository CHANGELOG style, or I have commented below to indicate which CHANGELOG exemption this PR falls under1.///).If you need help, consider asking for advice on the #hackers-new channel on Discord.
Note: The Flutter team is currently trialing the use of Gemini Code Assist for GitHub. Comments from the
gemini-code-assistbot should not be taken as authoritative feedback from the Flutter team. If you find its comments useful you can update your code accordingly, but if you are unsure or disagree with the feedback, please feel free to wait for a Flutter team member's review for guidance on which automated comments should be addressed.Footnotes
Regular contributors who have demonstrated familiarity with the repository guidelines only need to comment if the PR is not auto-exempted by repo tooling. ↩ ↩2 ↩3