115 lines
7.2 KiB
Markdown
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.
|