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

7.2 KiB

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:

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:

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.