layout/
accessibility_tree.rs1use accesskit::Role;
6use layout_api::LayoutNode;
7use log::trace;
8use rustc_hash::FxHashMap;
9use script::layout_dom::ServoLayoutNode;
10use servo_base::Epoch;
11use style::dom::{NodeInfo, OpaqueNode};
12
13struct AccessibilityUpdate {
14 accesskit_update: accesskit::TreeUpdate,
15}
16
17#[derive(Debug)]
18struct AccessibilityNode {
19 id: accesskit::NodeId,
20 accesskit_node: accesskit::Node,
21}
22
23#[derive(Debug)]
24pub struct AccessibilityTree {
25 nodes: FxHashMap<accesskit::NodeId, AccessibilityNode>,
26 accesskit_tree: accesskit::Tree,
27 tree_id: accesskit::TreeId,
28 epoch: Epoch,
29}
30
31impl AccessibilityUpdate {
32 fn new(tree: accesskit::Tree, tree_id: accesskit::TreeId) -> Self {
33 Self {
34 accesskit_update: accesskit::TreeUpdate {
35 nodes: Default::default(),
36 tree: Some(tree),
37 focus: accesskit::NodeId(1),
38 tree_id,
39 },
40 }
41 }
42
43 fn add(&mut self, node: &AccessibilityNode) {
44 self.accesskit_update
45 .nodes
46 .push((node.id, node.accesskit_node.clone()));
47 }
48}
49
50impl AccessibilityTree {
51 const ROOT_NODE_ID: accesskit::NodeId = accesskit::NodeId(0);
52
53 pub(super) fn new(tree_id: accesskit::TreeId, epoch: Epoch) -> Self {
54 let mut root_node = AccessibilityNode::new(AccessibilityTree::ROOT_NODE_ID);
56 root_node
57 .accesskit_node
58 .set_role(accesskit::Role::RootWebArea);
59 root_node
60 .accesskit_node
61 .add_action(accesskit::Action::Focus);
62
63 let mut tree = Self {
64 nodes: Default::default(),
65 accesskit_tree: accesskit::Tree::new(root_node.id),
66 tree_id,
67 epoch,
68 };
69 tree.nodes.insert(root_node.id, root_node);
70
71 tree
72 }
73
74 pub(super) fn update_tree(
75 &mut self,
76 root_dom_node: &ServoLayoutNode<'_>,
77 ) -> Option<accesskit::TreeUpdate> {
78 let mut tree_update = AccessibilityUpdate::new(self.accesskit_tree.clone(), self.tree_id);
79
80 let root_dom_node_id = Self::to_accesskit_id(&root_dom_node.opaque());
81 let root_node = self
82 .nodes
83 .get_mut(&AccessibilityTree::ROOT_NODE_ID)
84 .unwrap();
85 root_node
86 .accesskit_node
87 .set_children(vec![root_dom_node_id]);
88
89 tree_update.add(root_node);
90
91 self.update_node_and_children(root_dom_node, &mut tree_update);
92 Some(tree_update.accesskit_update)
93 }
94
95 fn update_node_and_children(
96 &mut self,
97 dom_node: &ServoLayoutNode<'_>,
98 tree_update: &mut AccessibilityUpdate,
99 ) {
100 let node = self.get_or_create_node_mut(dom_node);
103 let accesskit_node = &mut node.accesskit_node;
104
105 let mut new_children: Vec<accesskit::NodeId> = vec![];
106 for dom_child in dom_node.flat_tree_children() {
107 let child_id = Self::to_accesskit_id(&dom_child.opaque());
108 new_children.push(child_id);
109 }
110 if new_children != accesskit_node.children() {
111 accesskit_node.set_children(new_children);
112 }
113
114 if dom_node.is_text_node() {
115 accesskit_node.set_role(Role::TextRun);
116 let text_content = dom_node.text_content();
117 trace!("node text content = {text_content:?}");
118 accesskit_node.set_value(&*text_content);
120 } else if dom_node.as_element().is_some() {
121 accesskit_node.set_role(Role::GenericContainer);
122 }
123
124 tree_update.add(node);
125
126 for dom_child in dom_node.flat_tree_children() {
127 self.update_node_and_children(&dom_child, tree_update);
128 }
129 }
130
131 fn get_or_create_node_mut(&mut self, dom_node: &ServoLayoutNode<'_>) -> &mut AccessibilityNode {
132 let id = Self::to_accesskit_id(&dom_node.opaque());
133
134 self.nodes
135 .entry(id)
136 .or_insert_with(|| AccessibilityNode::new(id))
137 }
138
139 fn to_accesskit_id(opaque: &OpaqueNode) -> accesskit::NodeId {
140 accesskit::NodeId(opaque.0 as u64)
141 }
142
143 pub(crate) fn epoch(&self) -> Epoch {
144 self.epoch
145 }
146}
147
148impl AccessibilityNode {
149 fn new(id: accesskit::NodeId) -> Self {
150 Self {
151 id,
152 accesskit_node: accesskit::Node::new(Role::Unknown),
153 }
154 }
155}