layout: Add a basic accessibility tree implementation for web contents (#42338)

This change introduces the `accessibility_tree` module, containing code
to build an in-memory representation of a very basic accessibility tree
for web contents. Currently, the tree for a given document contains:
- a `RootWebArea` which has the document root node as its sole child,
- an `Unknown` node for the root DOM node,
- a `GenericContainer` node for each DOM element, and
- a `TextRun` node for each text node.

This allows us to make basic assertions about the tree contents in the
`accessibility` test by doing a tree walk to find text nodes and
checking their contents.

Right now, the tree is rebuilt from scratch when accessibility is
enabled and when a navigation occurs (via
`Constellation::set_frame_tree_for_webview()` sending
`ScriptThreadMessage::SetAccessibilityActive`); it's not responsive to
changes in the page.

This change also changes the way we handle updating the graft node
between the webview's accessibility tree and its top level pipeline's
accessibility tree.

Previously, `Constellation::set_frame_tree_for_webview()` would send a
`ConstellationToEmbedderMsg::DocumentAccessibilityTreeIdChange` method
informing the webview of the accesskit TreeId of the top-level pipeline.
However, this resulted in flaky timing as we couldn't depend on that
message being handled before the message containing the TreeUpdate from
the WebContents, which would lead to a panic as the new TreeId wasn't
grafted into the combined tree yet.

This change introduces an epoch value which flows from the
ConstellationWebview, where it's updated every time the
`active_top_level_pipeline_id` changes, to the layout accessibility
tree, and finally to the webview with each TreeUpdate. Whenever a
TreeUpdate arrives at the webview which has a newer epoch than the last
known epoch, the webview-to-contents graft node is updated before the
TreeUpdate is forwarded. If a TreeUpdate arrives at the webview with an
epoch _older_ than the last known epoch, it's dropped, as it must be for
a no-longer-active pipeline.

Fixes: Part of #4344

---------

Signed-off-by: delan azabani <dazabani@igalia.com>
Signed-off-by: Alice Boxhall <alice@igalia.com>
Co-authored-by: delan azabani <dazabani@igalia.com>
Co-authored-by: Luke Warlow <lwarlow@igalia.com>
This commit is contained in:
Alice
2026-04-14 15:36:57 +02:00
committed by GitHub
parent 2930beb4d1
commit d11fc3a1c6
18 changed files with 546 additions and 122 deletions

View File

@@ -678,6 +678,8 @@ impl RunningAppState {
for window in self.windows().values() {
for (_, webview) in window.webviews() {
// Activate accessibility in the WebView.
// There are two sites like this; this is the a11y activation site.
webview.set_accessibility_active(active);
}
}

View File

@@ -107,6 +107,8 @@ impl ServoShellWindow {
self.add_webview(webview.clone());
// If `self` is not in `state.windows`, our notify_accessibility_tree_update() will panic.
if state.accessibility_active() {
// Activate accessibility in the WebView.
// There are two sites like this; this is the WebView creation site.
webview.set_accessibility_active(true);
}
webview