AppFlowy-IO / AppFlowy-IO/appflowy-editor

Mobile selection auto-scroll can enter feedback loop / ANR when table focus changes

Abierto
#1,215 0 comentarios 0 reacciones 0 asignados Ver en GitHub
Lenguaje dominante
Dart
Estrellas
684
Forks
329
Métricas de merge de PR
Sin PR fusionados en 30 d

Descripción

### Description

After fixing the table row-height relayout loop from #1213 / #1214, the same mobile table interaction still exposed a second, independent hang in editor auto-scroll.

On a physical Android device, editing a table cell, tapping out of it, then tapping back into the table could ANR the app. With the microtask starvation fixed, the remaining symptom appeared as the page content/table rapidly vibrating until tapping out again, which indicates a repeat auto-scroll feedback loop.

This did not reproduce on the Pixel emulator or Chrome in my testing; it reproduced on a physical Galaxy S23+.

### Environment

- AppFlowy Editor: 6.1.0 / 6.2.0 code path
- Flutter: 3.41.2 stable
- Device: Galaxy S23+ (SM-S916U)
- OS: Android 16
- Renderer: Impeller / Vulkan
- Scenario: mobile editor, table cell focus/defocus, selection/caret update near table content

### Root cause

There are two related problems in the mobile auto-scroll path:

1. The vendored `EdgeDraggingAutoScroller._scroll` in `lib/src/flutter/scrollable_helpers.dart` recursively awaits itself:

```dart
if (_scrolling) {
await _scroll();
}
```

`ScrollPosition.moveTo` can complete synchronously, especially when the scroll position cannot make meaningful progress. In that case, the async recursion can become a tight microtask chain and starve frames/input.

2. Mobile selection changes call `startAutoScroll` even for ordinary caret/table focus updates. Those should be one-shot keep-visible nudges, not repeating auto-scroll. Otherwise, after moving repeat work to frame callbacks, the bug remains visible as rapid content vibration.

### Symbolicated ANR stack

A release APK built with split debug info produced this main-thread Dart stack shape:

```text
ChangeNotifier.notifyListeners
-> _RestorableScrollOffset.didUpdateValue
-> ScrollableState.saveOffset
-> ScrollPosition.didEndScroll
-> ScrollPositionWithSingleContext.jumpTo
-> ViewportOffset.moveTo
-> ScrollPosition.moveTo
-> EdgeDraggingAutoScroller._scroll
-> EdgeDraggingAutoScroller._scroll
-> _microtaskLoop
```

### Expected behavior

Table focus/defocus and caret movement should settle. Auto-scroll should repeat only during actual drag selection and should yield between ticks so frames/input can continue.

### Actual behavior

The editor can enter an auto-scroll feedback loop. On the physical Android device this manifested first as an ANR, and after partial mitigation as visible table/content vibration until tapping out.

### Proposed fix

- Schedule repeated auto-scroll ticks with `SchedulerBinding.scheduleFrameCallback` instead of recursive `await _scroll()`.
- Preserve no-progress termination so auto-scroll stops if `moveTo` does not advance the offset.
- Add a `repeat` flag to the editor auto-scroll service.
- Use repeating auto-scroll only for real mobile drag modes (`leftSelectionHandle`, `rightSelectionHandle`, `cursor`). Use one-shot auto-scroll for ordinary mobile selection/caret updates.

### Verification

On the physical Galaxy S23+:

- The original two-tap table interaction no longer ANRs.
- No new Android Dropbox ANR was written after the fix.
- Screenshots taken shortly after re-entering a table and again two seconds later were stable, with no continuing vibration.
- Manual selection around the table looked stable.

Guía de contribución

No hay ninguna guía de contribución indexada para este repositorio

Evaluación

Este issue todavía no se ha evaluado.

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.