Skip to content

Desktop app: accessibility peers still pin retired chat tabs and PR reader tabs on macOS #1688

Closed

Description

@alexeyzimarev

Follow-up to #1497. PR #1500's RetainedViewTemplate holds: on the installed 1.3.0 after 11 hours there is one WorkspaceView / TerminalControl / ChatTabView instead of 25, and gen2 is 103 MB instead of 437 MB. But the macOS accessibility bridge (AvaloniaUI/Avalonia#8240) pins whatever the templates inside the workspace rebuild per session. Root paths from a gcdump of that process:

[strong Handles] → MicroComShadow → Avalonia.Native.AvnAutomationPeer → ButtonAutomationPeer → Button
  → EffectiveValue<ICommand> → ReactiveCommand → ChatTabViewModel → WorkspaceViewModel
  → TerminalTabViewModel → XtermTerminalSurface / Utf8StreamDecoder (3 MB char[] each) / StringBuilder
[strong Handles] → MicroComShadow → Avalonia.Native.AvnAutomationPeer → ListItemAutomationPeer → TabStripItem
  → PullRequestReader → PullRequestContextViewModel → WorkspaceViewModel → …

Two of the three live WorkspaceViewModels were held only that way. Treating the native peers as a barrier, 228k objects / 35 MB of the 113 MB the dump captured were reachable through a peer alone: 2,681 composition visuals, 436 TextBlocks, 128 Buttons, 228 ToolCallItems, an XTerm buffer. The dump is lossy on the LOH, so that is a lower bound. Every session switch that an accessibility client has looked at adds another set.

Upstream: the native release fix (AvaloniaUI/Avalonia#22492) was closed in favour of AvaloniaUI/Avalonia#22519, which moves Avalonia.Native to MicroCom smart pointers. That PR targets main only and its author says it should not be backported to 12.1.x. The app is on 12.1.4, so nothing arrives before 12.2.

Proposal:

  • Ship the patched libAvaloniaNative.dylib built from the #22492 change against the 12.1.4 tag, copied over the package's dylib at publish time before signing, and remove it when the app moves to 12.2. The in-process accessibility walk repro showed 0 of 20 detached views surviving with it, 5 of 5 without.
  • As a bound on the damage, tear retired view models down on a switch (ChatTabViewModel.TeardownAsync exists; the terminal tab's buffers, feeds and the PR context should be dropped too), so a pinned button costs one control, not a session.

Sealing the accessibility tree at the window is not proposed: it removes VoiceOver, and WindowBaseAutomationPeer's hit-test and focus entry points are not virtual, so it would need an explicit IRootProvider re-implementation to be complete.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions