Fix Add-on Store freeze when navigating the list quickly (#20519)
Closes #17351
Summary of the issue:
NVDA freezes when navigating the Add-on Store list quickly, for example by holding an arrow key. The watchdog triggers and NVDA becomes unresponsive for several seconds at a time.
The cause is the details panel to the right of the list. Every time the selection changes, AddonDetails._refresh rebuilds that panel from scratch. Rebuilding the "Other details" rich text control appends its content in many small steps, and each append emits an EVENT_OBJECT_VALUECHANGE accessibility event on the control. NVDA processes every one of these events on the main thread. While navigating quickly the rebuilds pile up, the value change events flood the main thread, and the main thread saturates until the watchdog reports a freeze.
Description of user facing changes:
The Add-on Store stays responsive when navigating the list quickly. NVDA no longer freezes when holding an arrow key to move through the add-ons.
Description of developer facing changes:
None.
Description of development approach:
The details refresh in AddonDetails is now debounced. _updatedListItem no longer calls _refresh directly. Instead it calls _scheduleRefresh, which is decorated with the shared debounceLimiter from utils.debounce. It is configured as a pure trailing edge debounce, runImmediateFirstCall=False, with the named constant _REFRESH_DELAY_MS of 100 milliseconds. This reuses NVDA's existing debouncing architecture, the same approach as _scheduleFilter in browseMode. Rapid selection changes keep restarting the debounce, so the expensive rebuild runs only once the user settles on an add-on.
There is deliberately no immediate flush when focus leaves the list. Human reaction time between stopping and pressing tab is well above the 100 millisecond debounce. The rebuild has therefore almost always run before the details are reached, and any remaining lag is not perceptible. A flush would also force a rebuild on every list exit, including the common case where the details are already current. That would reintroduce the accessibility event traffic this fix removes.
Teardown is handled in _onDestroy. Destroy events bubble up from child controls, so the handler filters on evt.GetWindow() is self. The debouncer offers no way to cancel a scheduled call from outside, so on the panel's own destruction _onDestroy sets an _isBeingDestroyed flag and unregisters from selection updates. _refresh checks that flag first and returns early, so a still scheduled refresh becomes a no-op instead of touching controls that are being destroyed.
The root cause was confirmed with debug logging. Before the fix, holding an arrow key through the list produced about 37000 value change events on the details rich text control across roughly 740 selection changes, which is about 50 events per selection, together with a single freeze of about 4.6 seconds. After the fix, a comparable run with more navigation, about 1000 selection changes, produced about 2600 value change events, which is about 2.5 events per selection, and no freeze at all.
This PR was developed with AI assistance (Claude Code) with human review and manual testing.