Repository navigation
[web] Stop percent-decoding URLs written to browser history - #189835
auto-submit[bot] merged 8 commits into
Conversation
When the framework reports new route information via `routeInformationUpdated`, the web engine rebuilt the URL and ran `Uri.decodeComponent` over the whole string before handing it to the browser history. This stripped one level of percent-encoding from the address bar on every report: - `/item/foo%2Fbar` became `/item/foo/bar`, so refreshing or sharing the link no longer resolved to the same route. - Query values containing an encoded `&` (`?q=x%26y`) were corrupted into two separate parameters (`?q=x&y`), and encoded hashes (`%23`) turned into real fragment separators. - Routers that decode path parameters once (e.g. go_router) saw different parameter values for deep links vs. in-app navigation, because each report through the engine dropped an encoding level. Keep the scheme/authority stripping, but reassemble the URL from the raw (still encoded) path, query, and fragment components so the browser's address bar reflects exactly what the framework reported. Unlike the earlier attempt in flutter#176029, this also avoids re-encoding the query via `Uri(queryParameters:)`, which altered the reported encoding (e.g. `%20` becoming `+`), and adds a regression test. Fixes flutter#180373 Fixes flutter#171757 Fixes flutter#147857 Fixes flutter#155992
There was a problem hiding this comment.
Code Review
This pull request modifies EngineFlutterWindow to preserve the percent-encoding of the path, query, and fragment when extracting the path from a URI string, and adds a corresponding regression test. Feedback on the changes points out that Dart's Uri properties (path, query, and fragment) return decoded strings, which would fail to preserve the percent-encoding and cause the new test to fail. It is suggested to extract these components directly from the original URI string instead.
mdebbar
left a comment
There was a problem hiding this comment.
Thanks for the contribution!
|
autosubmit label was removed for flutter/flutter/189835, because - The status or check suite Google testing has failed. Please fix the issues identified (or deflake) before re-applying this label. |
|
I had to fix something in internal Google Testing. It should be mergeable now. Thanks again for contributing this fix! |

When the framework reports new route information via the
routeInformationUpdatednavigation message, the web engine rebuilds the URL and runs
Uri.decodeComponentover the whole string before handing it to the browser history (introduced in
flutter-team-archive/engine#40250). This strips one level of percent-encoding from the address
bar on every report:
/item/foo%2Fbar/item/foo/bar— refresh/share resolves to a different route/item/foo%2Fbar?q=x%26y?q=x&y— value splits into two query parameters?q=x%26y?color=%23ffffff?color=#ffffff— rest of the URL becomes a fragment?color=%23ffffffBecause routers decode path parameters exactly once, this also made
GoRouterState.pathParametersdiffer between in-app navigation and a subsequentbrowser refresh of the same URL: each round trip through the engine silently
dropped one encoding level.
Fix
handleNavigationMessagekeeps the scheme/authority stripping, but now reassemblesthe URL from the raw (still encoded)
path,query, andfragmentcomponentsinstead of decoding the result. The browser's address bar now reflects exactly what
the framework reported.
This supersedes #176029, which was closed for inactivity (missing tests). Unlike
that attempt, this PR also avoids reconstructing the query via
Uri(queryParameters:), which re-encoded values differently than reported(e.g.
%20in a query value came back as+), and it adds a regression test.Tests
Adds a regression test to
lib/web_ui/test/engine/routing_test.dartassertingthat
%20,%2F, and%26in path, query, and fragment survive the round tripinto browser history unchanged. The existing
routeInformationUpdated can handle uritest (scheme/authority stripping) is unaffected and still passes.Fixes #180373
Fixes #171757
Fixes #147857
Fixes #155992
cc @mdebbar (you triaged #176029 and invited a re-open — this picks that up with tests)
Pre-launch Checklist
///).If you need help, consider asking for advice on the #hackers-new channel on Discord.
If this change needs to override an active code freeze, provide a comment explaining why. The code freeze workflow can be overridden by code reviewers. See pinned issues for any active code freezes with guidance.
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.