Max Design

Published:

Note: The following steps represent my current understanding of how Chrome’s internal accessibility pipeline works. This overview is based on information from the Chromium open-source codebase, public documentation, and discussions from Chromium’s accessibility components. Any errors are my own.

Over the past weeks, I’ve written a series of articles that explain how events and the accessibility tree work at a high level. These include:

In those articles, we follow a clean, simple pipeline:

DOM mutation
↓
AXEvent
↓
Accessibility tree
↓
OS accessibility API
↓
Screen reader

This model is correct. But there is a lot more that happens between the DOM mutation and the moment an event appears in the accessibility tree.

And here’s the strange part: Chrome doesn’t have one accessibility tree, it has two!

These are:

  1. The hidden internal tree in Blink.
  2. The public-facing platform-neutral tree you normally interact with.

This article traces the full lifecycle of an accessibility event. From the moment the DOM changes, through Blink’s internal systems, through its internal accessibility tree, and finally back out into the BrowserAccessibility tree that screen readers query.

This is a highly technical look at how Chrome actually works.

Strap in. This is going to be a deep, wild ride.


Chrome’s accessibility event lifecycle

1. A DOM mutation occurs

A DOM mutation means something changes in the DOM: text, attributes, structure, visibility, layout, state, or content. These changes may happen with or without JavaScript.

Note: Steps 2-5 occur inside Blink’s internal accessibility tree.

2. Blink evaluates whether the mutation affects accessibility

Blink (Chrome’s rendering engine) uses its internal accessibility tree to check whether the mutation affects semantics such as:

If the change has no accessibility impact, the process stops here.

3. If it does affect accessibility, Blink marks the corresponding AX node as “dirty”

AX nodes belong to Blink’s internal accessibility tree. They represent semantic objects such as:

AX nodes are not DOM nodes — they are a separate structure derived from the DOM.

A node becomes dirty when its accessible representation must be refreshed, such as when:

Dirty nodes tell Blink which AX nodes must be re-serialized. This means that Blink generates a fresh AXNodeData snapshot for the node with all its current properties.

4. Blink creates an AXEvent describing what kind of accessibility change occurred

AXEvents are internal Blink signals that never leave the renderer process.

AXEvents label the semantic type of change and Blink sends these event types to the browser process, which uses them to determine which OS accessibility events to fire.

They are not JavaScript events and not OS events.

5. Blink re-serializes dirty AX nodes into AXNodeData to capture what actually changed

Each dirty AX node is converted into AXNodeData.

An AXNodeData is platform-neutral snapshot that includes:

AXNodeData is a snapshot of a Blink AX node, converted into a simple data structure so it can be sent from the renderer process to the browser process using Chrome’s internal messaging system (IPC).

6. Blink sends AXNodeData to the browser process, which delivers them to BrowserAccessibilityManager

This is the moment when data leaves Blink’s AX tree and enters Chrome’s BrowserAccessibility tree in the browser process.

Blink sends the updated AXNodeData and a list of event types to fire.

Blink’s role ends here.

Note: Steps 7–10 occur inside the BrowserAccessibility tree.

7. BrowserAccessibilityManager updates Chrome’s platform-neutral accessibility tree

BrowserAccessibilityManager maintains Chrome’s second accessibility tree - the BrowserAccessibility tree.

Using AXNodeData, it:

This is the accessibility tree Chrome exposes to operating systems.

8. Each BrowserAccessibility node has a platform-specific accessibility wrapper

Every node in the BrowserAccessibility tree is paired with a persistent platform wrapper, such as:

These wrappers translate Chrome’s platform-neutral accessibility data into the specific OS accessibility API.

9. The platform wrapper is notified and emits the corresponding platform accessibility event

The wrapper translates the event into the correct platform-specific accessibility event and fires it through the OS API.

Accessibiltiy events could include:

These are the events assistive technologies actually receive.

10. Screen readers query the OS accessibility API, which retrieves information from Chrome’s BrowserAccessibility tree

Screen readers such as VoiceOver, NVDA, JAWS, Narrator, and TalkBack:

This is where the user finally experiences the result of the original DOM mutation.

So the more detailed diagram is now:

DOM mutation
↓
Blink AX tree
↓
Blink accessibility updates (AXEvents + AXNodeData)
↓
BrowserAccessibility tree
↓
Platform wrappers
↓
OS API
↓
Screen readers

We are done!


What’s the difference between the trees?

Blink AX Tree (internal engine tree)

This is Blink’s own accessibility representation of the DOM. It is tightly connected to:

It’s used to detect accessibility-relevant changes and produce AXNodeData.

Key features:

BrowserAccessibility Tree (platform-neutral browser tree)

This is Chrome’s OS-facing accessibility tree. It stores:

This tree is what Chrome exposes to operating systems such as:

Key features

This tree has permission to communicate with OS accessibility APIs.

Which tree is used by assistive technologies?

Screen readers only interact with the BrowserAccessibility tree.

But everything they read originates in Blink’s AX tree. The data simply travels through AXNodeData to reach the browser tree.

So, both trees are essential. They just play completely different roles.


Conclusion

Every time a screen reader announces something, it’s the result of two accessibility trees working together behind the scenes.

Note: Chrome’s accessibility architecture is complex and always evolving. If you have corrections or additional insight, I’d be grateful to hear from you.