Files
cthulhu/docs/testing/browser-navigation-performance.md

115 lines
7.2 KiB
Markdown

# Document navigation performance and validation
Browse navigation reuses an immutable document view and an object-to-index
lookup owned by `AccessibleTree`. `DocumentCursor` reuses the view's caret ranges
and visual-line ownership map. Focus synchronization, heading/link searches,
position lookup, linked-heading lookup and replacement-parent bookkeeping use
that index instead of rebuilding maps or scanning the entire document per key.
`Replace`, object removal and destructive child invalidation retire the cached
view. Pending semantic, text, geometry and child-order changes keep the view's
immutable data but mark the live tree entries dirty. Before navigation reads or
selects content, it validates the objects it traverses and their relevant
ancestors. Dirty skipped candidates are validated too, so navigation cannot
silently omit newly readable content. A local move within one live text object
can proceed through a parent's unrelated insertion; crossing objects requires
fresh child order. Table-row summaries also validate their contributing sibling
cells using the same bounded dependency list used to derive the row text.
Failed validation preserves the reading position and uses the existing bounded
recapture/retry path. Successful native text remains valid for the requested
announcement if the provider removes its object immediately afterwards; retrying
that completed move would skip text. Activation, element lists, native selection
resolution and captured-line assembly retain complete-document validation.
`Document` still returns an independently owned node slice; callers of
`DocumentView` and `NavigationDocument` must treat the shared data as read-only.
Tree access and cache publication use the tree mutex, and native object lifetime
and GLib ownership stay with the adapter.
On 2026-10-24, three sequential 200 ms runs on an AMD Ryzen 9 PRO 5945 with
Go 1.27.3 compared the unchanged `133953e` source through a Go overlay with
these changes. The same benchmark source ran against both versions. Median
results for the 10,000-content-object fixture were:
| Operation | Before | After | Bytes allocated before | Bytes allocated after |
| --- | ---: | ---: | ---: | ---: |
| Worker heading forward/back pair | 87.476 ms | 0.053 ms | 276,775,952 | 51,650 |
| Character forward/back pair | 53.536 ms | 0.014 ms | 124,261,980 | 16,072 |
| Heading forward/back pair | 43.129 ms | 0.035 ms | 142,691,897 | 18,080 |
| Heading wrapping pair | 44.895 ms | 0.032 ms | 142,687,798 | 18,080 |
| Owned document snapshot | 14.747 ms | 5.421 ms | 54,801,125 | 11,723,115 |
| Captured line assembly | 15.297 ms | 3.095 ms | 54,810,445 | 182,019 |
These measure owned Go data and fake-provider calls, excluding native browser
IPC, initial capture, speech delivery and audible latency. Allocation figures
are traffic per operation, not retained process memory. Snapshot/line results
include an amortized first index build; navigation setup precedes timing.
Raw outputs are `/tmp/cthulhu-web-performance-before.txt` and
`/tmp/cthulhu-web-performance-after.txt`; medians are retained in
`/tmp/cthulhu-web-performance-medians.json`. Performance remains informational,
with no timing gate added to tests.
Run the affected correctness and race checks in a private session:
```sh
python3 tests/native_test_session.py env GOCACHE=/tmp/cthulhu-cache go test ./internal/atspi ./internal/review ./internal/app -count=1
python3 tests/native_test_session.py env GOCACHE=/tmp/cthulhu-cache go test -race ./internal/atspi ./internal/review ./internal/app -count=1
go vet ./internal/atspi ./internal/review ./internal/app
```
The tests cover cached-view ownership, concurrent replacement, retired views,
cancellation, unrelated dirty branches, stale traversal and unchanged reading
positions after failures. Existing regressions also cover replacement recovery,
generation-checked activation, table navigation, native selections, passwords,
inline text, named links and browser-chrome round trips. Two existing recapture
tests now distinguish continued reading within a live leaf from crossing dirty
child order, rather than requiring a recapture for every document mutation.
Run the existing browser fixtures and actual-reader navigation fixture:
```sh
CTHULHU_NATIVE_BROWSER_TESTS=replaced-feed,generatedlinks,replacement,fragments,tabs,namedlinks,linkedheadings,tables,dynamic,document,editing,selection,modes python3 tests/native_browser.py
go build -o /tmp/cthulhu-browser-performance ./cmd/cthulhu
CTHULHU_BINARY=/tmp/cthulhu-browser-performance python3 tests/native_browser_navigation.py
```
The actual-reader fixture checks 76 navigation commands while page text changes,
including forward/reverse headings, line/character/word movement and custom
activation. It records speech protocol requests in a private X11/browser session.
It does not establish audible output or physical keyboard/Braille behavior.
An initial optimized run passed the entire fixture. A final-binary run completed
all 76 navigation commands and both custom activations, then failed its final
pointer-handoff assertion: the click at screen coordinates 0,0 produced no
observed shared AT-SPI pointer press. The focused native pointer test passed.
Keep this separate from the verified navigation sequence; it remains an open
harness/delivery check. The unchanged original binary reproduced the same final
pointer failure after completing its navigation sequence, so this failure
predates the performance changes. Evidence is retained in
`/tmp/cthulhu-browser-navigation-56h2cw3n` (final binary),
`/tmp/cthulhu-browser-navigation-i2jxxobv` (original binary), and
`/tmp/cthulhu-browser-navigation-mduikajr` (initial complete optimized pass).
The browser `modes` fixture remains an intermittent gate. During this change it
passed in an isolated optimized run, but mixed runs failed once on page return
and once while expecting typed chrome text. The original source passed isolated
and tables/selection/modes comparison runs; these comparisons do not conclusively
attribute the intermittent failures. Preserve their logs and report the open
gate rather than describing the whole browser suite as consistently passing.
For human acceptance, use the actual reported Reddit view and a long updating
page. Navigate headings in both directions through wrapping, read post/comment
text with arrows and word/character commands, expand/collapse comments, load
more content, type and select in an editor, activate links, and return from the
address bar and another tab. Check that stale/replaced targets never activate a
different control. On a table, change a sibling cell and verify full-row speech
and Braille refresh. Check Steam browse navigation and search/Tab behavior after
an application/workspace round trip. Listen for skipped/repeated text and verify
physical Braille where available. Preserve the current log and obtain approval
before replacing the active reader.
The first native document capture still enumerates the captured tree, the
30,000-object safety limit still applies, and generic named-clickable filtering
is unchanged. These performance changes do not establish a fix for Reddit's
specific reported content problem. Review the diff and complete the live checks
before submitting under the project's AI-assisted contribution policy.