Repository navigation
[beta] [web] Cancel touch pointers WebKit abandons mid-gesture - #190231
auto-submit[bot] merged 1 commit into
Conversation
Fixes flutter#188781 **Problem** On iOS 27, WebKit stops dispatching pointer events for a touch it promotes to a native gesture, such as dragging the caret in a text field, and delivers neither `pointerup` nor `pointercancel`. The framework is left holding a pointer that never lifts, which wedges `BaseTapAndDragGestureRecognizer` so every later tap is silently dropped: the field never regains focus and the keyboard never returns. Affects every iOS 27 browser, since they all run WebKit; iOS 26 is unaffected. **Fix** `_PointerAdapter` tracks which touch devices are down and, on `touchend` and `touchcancel`, cancels any the browser no longer lists in `touches`. Gated to iOSWebKit. **Demo** Before: https://flutter-demo-47-before.web.app After: https://flutter-demo-47-after.web.app **Repro steps:** type in the field, long-press the caret and drag it, lift, then tap the field. Before, the field is permanently dead after a few tries. After, it recovers.
|
This pull request was opened from and to a release candidate branch. This should only be done as part of the official Flutter release process. If you are attempting to make a regular contribution to the Flutter project, please close this PR and follow the instructions at Tree Hygiene for detailed instructions on contributing to Flutter. Reviewers: Use caution before merging pull requests to release branches. Ensure the proper procedure has been followed. |
There was a problem hiding this comment.
Code Review
This pull request introduces a mechanism to detect and cancel abandoned touch pointers on iOS WebKit by reconciling tracked down touch devices against the active touches reported on the surface during touchend and touchcancel events. It also adds helper methods to flush the click debouncer queue before sending synthetic cancel events, along with comprehensive unit tests. The reviewer suggests also registering the _cancelAbandonedTouches safety net on touchstart events to clean up any stale pointers at the very beginning of a subsequent touch interaction.
| // Safety net for touches the browser abandons without a `pointerup` or a | ||
| // `pointercancel`. See [_cancelAbandonedTouches]. | ||
| addEventListener(_globalTarget, 'touchend', (DomEvent event) { | ||
| _cancelAbandonedTouches(event as DomTouchEvent); | ||
| }); | ||
| addEventListener(_globalTarget, 'touchcancel', (DomEvent event) { | ||
| _cancelAbandonedTouches(event as DomTouchEvent); | ||
| }); |
There was a problem hiding this comment.
Currently, _cancelAbandonedTouches is only registered for touchend and touchcancel events. If a touch is abandoned and its touchend is dropped by the browser, the abandoned pointer will remain active in the framework during the entire active phase of the next touch gesture (until its touchend fires). This can interfere with gesture recognition for the new gesture (e.g., interpreting a single-finger drag as a multi-finger gesture).
Registering _cancelAbandonedTouches on touchstart as well allows cleaning up the abandoned pointer at the very beginning of the next touch interaction, before the new pointer's pointerdown event is dispatched to the framework.
// Safety net for touches the browser abandons without a `pointerup` or a
// `pointercancel`. See [_cancelAbandonedTouches].
addEventListener(_globalTarget, 'touchstart', (DomEvent event) {
_cancelAbandonedTouches(event as DomTouchEvent);
});
addEventListener(_globalTarget, 'touchend', (DomEvent event) {
_cancelAbandonedTouches(event as DomTouchEvent);
});
addEventListener(_globalTarget, 'touchcancel', (DomEvent event) {
_cancelAbandonedTouches(event as DomTouchEvent);
});|
autosubmit label was removed for flutter/flutter/190231, because - The status or check suite Dashboard Checks has failed. Please fix the issues identified (or deflake) before re-applying this label. |
effb186
into
flutter:flutter-3.47-candidate.0
This pull request cherry-picks #189608 into the beta release branch (flutter-3.47-candidate.0) so the fix ships in the next stable release.
Issue Link:
#188781
Impact Description:
P0 regression. On iOS 27, which affects every browser since they all run WebKit, a text field in a Flutter web app can become permanently unresponsive after a common gesture. When the user long-press-drags the selection caret and
lifts, WebKit abandons the touch without delivering
pointeruporpointercancel, leaving the framework holding a pointer that never lifts. This wedges the tap-and-drag gesture recognizer, so every later tap is silently dropped: the field never regains focus and the keyboard never returns, and the user can no longer type. It breaks text input in shipping production web apps. iOS 26 is unaffected.Changelog Description:
[flutter/188781] When dragging the text selection caret on iOS 27 web browsers, text fields could become permanently unresponsive to taps and never regain focus; they now recover.
Workaround:
No in-app workaround. Reloading the page temporarily restores the field, but it breaks again the next time the gesture occurs.
Risk:
The change is small and strictly gated to iOS WebKit. It only cancels touch pointers the browser has already dropped from its
toucheslist, so it cannot affect other platforms or normal pointer handling.Test Coverage:
The fix adds 341 lines of engine unit tests in
pointer_binding_test.dartcovering the touch-cancel logic.Validation Steps:
Demo before: https://flutter-demo-47-before.web.app
Demo after: https://flutter-demo-47-after.web.app