[AppKit Gestures] Selection-drag autoscroll to the window edge on macOS
macOS의 텍스트 인터랙션은 기존 EventHandler 경로와 NSTextSelectionManager 기반의 AppKit-gestures 경로, 두 가지로 분리되어 있습니다. AppKit 경로에서는 UI process가 gesture 인식을 담당하고, extent-point 업데이트를 IPC를 통해 web process로 전달합니다. Web process는 scroll 상태를 관리하면서 scroll 알림을 역방향으로 전송하는데, Autoscroll은 web process 내부의 AutoscrollController가 50ms Selection 타이머로 구동합니다. iOS에서는 UIKit의 UITextAutoscrolling 프로토콜이 두 경로를 연결해 주지만, AppKit에는 이에 해당하는 메커니즘이 없습니다.
Source/WebCore/page/EventHandler.cpp
이 commit은 기존 iOS autoscroll 구현을 재활용하여, AppKit-gestures 경로에서 macOS의 selection-drag autoscroll을 구현합니다. 이를 위해 세 가지 변경이 추가되었습니다. 먼저 EventHandler에 edge-band 근접 감지 함수(isPointNearSelectionAutoscrollEdge)가 추가되었고, updateSelectionForMouseDrag에는 억제 guard가 적용되었습니다. 아울러 WKTextSelectionController에 UI process 측 re-extension hook이 추가되어, drag 진행 중 scroll 이벤트마다 호출됩니다. Web process는 extent-point IPC가 수신될 때마다 edge 근접 여부를 평가해 autoscroll 타이머를 시작하거나 취소합니다. scroll이 발생하면 UI process에 알림을 전송하고, UI process는 그 알림을 받아 새로 드러난 콘텐츠 영역으로 selection을 다시 확장합니다.
Significance
텍스트 selection을 위한 UI process와 web process 간의 양방향 cross-process feedback loop가 새롭게 도입되었습니다. IPC 경로, 좌표 변환 로직, 타이머 기반 state machine 모두 macOS에는 기존에 없던 구조입니다.
Audit directions
m_isAutoscrolling guard는 활성화된 상태에서 web process 측의 모든 selection 업데이트를 조용히 차단합니다. selection과 무관한 autoscroll(예: scrollable container에서의 일반 mouse-drag autoscroll)이 이 flag를 설정하는 경로가 있다면, selection 확장도 함께 억제될 가능성이 있습니다.
Cross-process feedback loop에는 명시적인 반복 횟수 제한이 없습니다(UI가 extent 전송 → web이 scroll → UI가 re-extend → 반복). 이 루프를 멈추는 유일한 조건은 isPointNearSelectionAutoscrollEdge가 false를 반환하는 것입니다. 빠른 zoom이나 iframe resize 중에 visible rect가 오래된 상태라면, 루프가 계속 반복될 가능성이 있습니다.
zoomScale 값은 edge hot-zone 크기와 scroll 속도를 나누는 데 사용되며, 상한/하한 제한이 보이지 않습니다. scale이 0에 근접하는 극단적인 zoom 상태에서는 hot-zone이 과도하게 커지고, 정수 나눗셈에서 overflow가 발생할 가능성이 있습니다. 해당 조건에서의 동작을 점검할 필요가 있습니다.
dragOriginInRootView는 UI process에서 drag 단위로 저장되며, web process의 hysteresis origin으로 사용됩니다. 두 번째 gesture가 시작되거나 autoscroll 도중 drag가 재사용되는 경우, IPC 전송 전에 origin이 올바르게 초기화되는지 점검할 필요가 있습니다.
마지막으로, web process의 50ms 타이머와 UI process의 reextendSelectionForAutoscrollIfNeeded는 동시에 동작하며 IPC 전달 순서가 보장되지 않습니다. cancel IPC가 큐에 삽입된 이후 web process가 이를 처리하기 전에 타이머가 발화하면, 대응하는 UI drag 없이 타이머가 활성 상태로 남는 상황이 이론적으로 가능합니다. 이 경우도 점검할 필요가 있습니다.