Class RecordingAccessibilityBridge
- All Implemented Interfaces:
AccessibilityBridge
It keeps an element registry of its own — a set of node identifiers — because two of the obligations the real bridges have are about exactly that and are otherwise untestable without a platform: an event budget that collapses has to leave a bridge able to release everything that went away, and a scene bound over a live window has to leave it holding nothing at all. A double that only counted calls could not fail either.
-
Nested Class Summary
Nested classes/interfaces inherited from interface limn.backend.AccessibilityBridge
AccessibilityBridge.Host -
Field Summary
FieldsModifier and TypeFieldDescriptionintHow many hosts it has been given, and the one it holds.The identifiers this bridge would be holding platform objects for.final List<AccessibleEvent> Every event handed over, in order.@Nullable AccessibilityBridge.HostbooleanWhatisListening()answers; a test turns the platform on and off with it.booleanWhatneedsPrimingPublish()answers.booleanWhatneedsRootBeforeTheFirstFrame()answers.final List<AccessibleTree> Every tree handed over, newest last.Whether each of those was handed over with the platform on the stack.Fields inherited from interface limn.backend.AccessibilityBridge
NONE -
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptionvoidattach(AccessibilityBridge.Host newHost) Hands the bridge the scene-side half, replacing any host it already held.voidclear()Forgets every tree and event recorded so far, so a test can assert on what follows.longcountOf(AccessibleEvent.Type type) voiddetach()The window is going away: raises a window-closed, then empties whatever it was holding.voidemit(AccessibleEvent event) One coalesced event, naming a node in the tree just published.eventsOf(AccessibleEvent.Type type) @Nullable AccessibleEventfirst(AccessibleEvent.Type type) booleanWhether any assistive technology is listening.static RecordingAccessibilityBridgeA bridge that is listening from the start: what a component test always wants, since it is asking what an assistive technology would be told.booleanWhether this bridge needs one tree on the scene's first frame even though nothing is listening yet, because its own listening gate cannot open until it has elements to offer.booleanWhether this bridge is owed the window's own node the moment a scene binds, because it can be asked whether this window has accessibility at all before the first frame has run.voidpublish(AccessibleTree tree, boolean isReentrant) Hands over a fresh whole-window snapshot.tree()Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, waitMethods inherited from interface limn.backend.AccessibilityBridge
frameEnded
-
Field Details
-
listening
public boolean listeningWhatisListening()answers; a test turns the platform on and off with it. -
needsPriming
public boolean needsPrimingWhatneedsPrimingPublish()answers. -
needsRootAtBind
public boolean needsRootAtBindWhatneedsRootBeforeTheFirstFrame()answers. -
published
Every tree handed over, newest last. -
reentrant
Whether each of those was handed over with the platform on the stack. -
events
Every event handed over, in order. -
elements
The identifiers this bridge would be holding platform objects for. -
attachments
public int attachmentsHow many hosts it has been given, and the one it holds. -
host
-
-
Constructor Details
-
RecordingAccessibilityBridge
public RecordingAccessibilityBridge()
-
-
Method Details
-
listening
A bridge that is listening from the start: what a component test always wants, since it is asking what an assistive technology would be told. The scene's own gate, that a quiet frame costs nothing when nothing is listening, is the scene package's to prove, with the plain constructor.- Returns:
- a recording bridge whose platform is on
-
isListening
public boolean isListening()Description copied from interface:AccessibilityBridgeWhether any assistive technology is listening.Asked once per frame on the publish path, and on the two rare paths that have to know whether spending a frame is worth it at all. It must therefore be cheap: one platform call, or a flag the bridge already keeps.
It is never "did a client ask us something recently." A gate of that shape inverts the contract on all three platforms, because a platform event is a push a client waits on: one of the three screen readers registers for a focus change and then calls nothing at all until it fires. Tab twice quickly with such a gate and the toolkit goes silent — the client only asks after an event, and the event only fires after the client asks.
- Specified by:
isListeningin interfaceAccessibilityBridge- Returns:
- whether anything is reading this window
-
needsPrimingPublish
public boolean needsPrimingPublish()Description copied from interface:AccessibilityBridgeWhether this bridge needs one tree on the scene's first frame even though nothing is listening yet, because its own listening gate cannot open until it has elements to offer.Asked once, when a scene binds. True where the honest gate is "someone has asked", which cannot open before the platform has been handed something to ask about: macOS, which has no equivalent of "is anyone listening" at all, and Windows, whose process-wide flag is only reliable in the negative and whose per-window half needs an element for a client to reach. False on Linux, which can ask its desktop directly and so pays no walk for a window an assistive technology never touches.
- Specified by:
needsPrimingPublishin interfaceAccessibilityBridge- Returns:
- whether this bridge is owed one tree on the first frame regardless
-
needsRootBeforeTheFirstFrame
public boolean needsRootBeforeTheFirstFrame()Description copied from interface:AccessibilityBridgeWhether this bridge is owed the window's own node the moment a scene binds, because it can be asked whether this window has accessibility at all before the first frame has run.Asked once, when a scene binds, and true on one platform. There a client asks the window itself, in a message, and a window that answers "nothing here" is not asked again: the client subscribes to no events, and every event raised for the rest of that window's life is delivered to nobody while the window keeps answering every question it is asked. Measured on the Windows 11 guest, 2026-09-16: the reader's first ask reaches the window 36-116 ms after the bridge goes in front of the window procedure and is answered 50-141 ms in, its second follows within milliseconds, and the first frame publishes 305-523 ms later — so ten runs of one build split four spoken and six silent on nothing but whether a second ask happened to arrive after that first publish (it did in two runs, ~200 ms late, and in two more the reader simply never asked twice). One
0answer before the first real one and the reader speaks; two and it is silent, in ten runs out of ten (readings/phase5-windows-diagnosis/evidence-table.txt).What it is owed is the window node alone, and not the scene. No layout has run at a bind, so every widget is a zero-size rectangle at the origin and a tree of those is worse than none — it reads perfectly and hit-tests nowhere. The window's role, its title and the size it already has are true at that instant, and the identifier they are published under is the one every later walk reuses, so the element a client subscribes to here is the element the contents arrive under.
- Specified by:
needsRootBeforeTheFirstFramein interfaceAccessibilityBridge- Returns:
- whether this bridge is owed the window's own node the moment a scene binds
-
publish
Description copied from interface:AccessibilityBridgeHands over a fresh whole-window snapshot. User-interface thread.reentrantis true when the scene built this tree from inside the platform's own callback, with the platform on the stack holding elements this bridge vended. A reentrant publish stores the tree and answers from it, and does nothing else: it must not destroy or release anything in its element registry, must not re-push the top of its tree, and must not drain its event queue — each of those would act on the objects the caller is standing on. Everything it defers is owed by the next ordinary frame, which the scene has already asked for.- Specified by:
publishin interfaceAccessibilityBridge- Parameters:
tree- the window as it now standsisReentrant- whether the platform is on the stack
-
emit
Description copied from interface:AccessibilityBridgeOne coalesced event, naming a node in the tree just published. User-interface thread.Enqueue it; do not raise it. A difference between two frames of a scrolling list or a dragged slider can be hundreds of nodes wide, and every raise is a cross-process call: a bridge that raised them here would spend the frame budget inside the platform, which is precisely the stall that makes a window unreadable to the user it is being read to.
- Specified by:
emitin interfaceAccessibilityBridge- Parameters:
event- what happened
-
attach
Description copied from interface:AccessibilityBridgeHands the bridge the scene-side half, replacing any host it already held.A scene can be bound over a live window and the outgoing one never learns it was, so the bridge is the only object that knows there was a tree to close. That is why the window pair is raised here rather than by the caller: an implementation empties its element registry and raises a window-closed for the host it is replacing, if there was one, then raises a window-opened for the incoming one. On a first attach only the second is raised.
It builds and pushes no tree, on any platform. At this moment the scene has never laid out, so every box in it would be a zero-size rectangle at the origin.
- Specified by:
attachin interfaceAccessibilityBridge- Parameters:
newHost- what the scene answers with
-
detach
public void detach()Description copied from interface:AccessibilityBridgeThe window is going away: raises a window-closed, then empties whatever it was holding.- Specified by:
detachin interfaceAccessibilityBridge
-
clear
public void clear()Forgets every tree and event recorded so far, so a test can assert on what follows. -
tree
- Returns:
- the most recently published tree, or the empty one
-
eventsOf
- Parameters:
type- the kind to look for- Returns:
- every event of that kind, in the order handed over
-
countOf
- Parameters:
type- the kind to count- Returns:
- how many events of that kind have been handed over
-
first
- Parameters:
type- the kind to look for- Returns:
- the first event of that kind, or
null
-