Skip to main content

xa11y_core/
provider.rs

1use crate::element::ElementData;
2use crate::error::Result;
3use crate::event_provider::Subscription;
4use crate::selector::{matches_simple, Combinator, Selector, SelectorGroup, SelectorSegment};
5
6/// Platform backend trait for accessibility tree access.
7///
8/// Providers implement lazy, on-demand tree navigation. Elements are identified
9/// by their [`ElementData`] (which contains a provider-specific `handle` for
10/// looking up the underlying platform object).
11///
12/// # Action model
13///
14/// Common actions are first-class methods with proper typed signatures.
15/// Platform-specific or custom actions use [`perform_action`](Self::perform_action)
16/// as an escape hatch — it takes a `snake_case` action name string.
17///
18/// Providers should check platform permissions in their constructor (`new()`)
19/// and return [`Error::PermissionDenied`](crate::Error::PermissionDenied) if
20/// required permissions are not granted.
21pub trait Provider: Send + Sync {
22    // ── Tree navigation ─────────────────────────────────────────────
23
24    /// Get direct children of an element.
25    ///
26    /// If `element` is `None`, returns top-level application elements.
27    fn get_children(&self, element: Option<&ElementData>) -> Result<Vec<ElementData>>;
28
29    /// Get the parent of an element.
30    ///
31    /// Returns `None` for top-level (application) elements.
32    fn get_parent(&self, element: &ElementData) -> Result<Option<ElementData>>;
33
34    /// Search for elements matching a selector.
35    ///
36    /// The selector is already parsed by the core — providers match against it
37    /// during traversal and can prune subtrees that can't match.
38    ///
39    /// If `root` is `None`, searches from the system root (all applications).
40    /// If `limit` is `Some(n)`, stops after finding `n` matches.
41    /// If `max_depth` is `Some(d)`, does not descend deeper than `d` levels.
42    ///
43    /// The default implementation traverses via [`get_children`](Self::get_children).
44    fn find_elements(
45        &self,
46        root: Option<&ElementData>,
47        selector: &Selector,
48        limit: Option<usize>,
49        max_depth: Option<u32>,
50    ) -> Result<Vec<ElementData>> {
51        crate::selector::find_elements_in_tree(
52            |el| self.get_children(el),
53            root,
54            selector,
55            limit,
56            max_depth,
57        )
58    }
59
60    /// Search for elements matching any clause of a comma-separated selector
61    /// group.
62    ///
63    /// Forwards to [`find_elements`](Self::find_elements) once per clause and
64    /// merges results into the union, deduplicated by element identity and
65    /// returned in document order. Single-clause groups short-circuit to a
66    /// straight `find_elements` call, so providers that override the
67    /// optimized single-clause path keep their fast path.
68    fn find_elements_group(
69        &self,
70        root: Option<&ElementData>,
71        group: &SelectorGroup,
72        limit: Option<usize>,
73        max_depth: Option<u32>,
74    ) -> Result<Vec<ElementData>> {
75        if group.clauses.len() == 1 {
76            return self.find_elements(root, &group.clauses[0], limit, max_depth);
77        }
78        // Each clause goes through the provider's own (possibly optimized)
79        // `find_elements`. The merge below walks once more via
80        // `get_children` to recover document order across clauses.
81        let mut clause_results = Vec::with_capacity(group.clauses.len());
82        for clause in &group.clauses {
83            clause_results.push(self.find_elements(root, clause, None, max_depth)?);
84        }
85        crate::selector::merge_clause_results_in_document_order(
86            |el| self.get_children(el),
87            root,
88            clause_results,
89            limit,
90            max_depth,
91        )
92    }
93
94    /// Narrow candidates through remaining selector segments (Child/Descendant
95    /// combinators), deduplicate, apply final :nth and limit.
96    fn narrow_multi_segment(
97        &self,
98        mut candidates: Vec<ElementData>,
99        segments: &[SelectorSegment],
100        max_depth: u32,
101        limit: Option<usize>,
102    ) -> Result<Vec<ElementData>> {
103        for segment in segments {
104            let mut next_candidates = Vec::new();
105            for candidate in &candidates {
106                match segment.combinator {
107                    Combinator::Child => {
108                        let children = self.get_children(Some(candidate))?;
109                        for child in children {
110                            if matches_simple(&child, &segment.simple) {
111                                next_candidates.push(child);
112                            }
113                        }
114                    }
115                    Combinator::Descendant => {
116                        let sub_selector = Selector {
117                            segments: vec![SelectorSegment {
118                                combinator: Combinator::Root,
119                                simple: segment.simple.clone(),
120                            }],
121                        };
122                        let mut sub_results = self.find_elements(
123                            Some(candidate),
124                            &sub_selector,
125                            None,
126                            Some(max_depth),
127                        )?;
128                        next_candidates.append(&mut sub_results);
129                    }
130                    Combinator::Root => unreachable!(),
131                }
132            }
133            let mut seen = std::collections::HashSet::new();
134            next_candidates.retain(|e| seen.insert(e.handle));
135            candidates = next_candidates;
136        }
137
138        // Apply :nth on last segment
139        if let Some(nth) = segments.last().and_then(|s| s.simple.nth) {
140            if nth <= candidates.len() {
141                candidates = vec![candidates.remove(nth - 1)];
142            } else {
143                candidates.clear();
144            }
145        }
146
147        if let Some(limit) = limit {
148            candidates.truncate(limit);
149        }
150
151        Ok(candidates)
152    }
153
154    // ── Common actions ──────────────────────────────────────────────
155
156    /// Click / tap / invoke the element.
157    fn press(&self, element: &ElementData) -> Result<()>;
158
159    /// Set keyboard focus to the element.
160    fn focus(&self, element: &ElementData) -> Result<()>;
161
162    /// Remove keyboard focus from the element.
163    fn blur(&self, element: &ElementData) -> Result<()>;
164
165    /// Toggle a checkbox or switch.
166    fn toggle(&self, element: &ElementData) -> Result<()>;
167
168    /// Select an item in a list, tab group, or menu.
169    fn select(&self, element: &ElementData) -> Result<()>;
170
171    /// Expand a collapsible element (combo box, tree item, disclosure).
172    fn expand(&self, element: &ElementData) -> Result<()>;
173
174    /// Collapse an expanded element.
175    fn collapse(&self, element: &ElementData) -> Result<()>;
176
177    /// Show the element's context menu or dropdown.
178    fn show_menu(&self, element: &ElementData) -> Result<()>;
179
180    /// Increment a slider or spinner by one step.
181    fn increment(&self, element: &ElementData) -> Result<()>;
182
183    /// Decrement a slider or spinner by one step.
184    fn decrement(&self, element: &ElementData) -> Result<()>;
185
186    /// Scroll the element into the visible area.
187    fn scroll_into_view(&self, element: &ElementData) -> Result<()>;
188
189    // ── Typed operations ────────────────────────────────────────────
190
191    /// Set the text value of the element.
192    fn set_value(&self, element: &ElementData, value: &str) -> Result<()>;
193
194    /// Set the numeric value of the element (slider, spinner).
195    fn set_numeric_value(&self, element: &ElementData, value: f64) -> Result<()>;
196
197    /// Insert text at the current cursor position.
198    fn type_text(&self, element: &ElementData, text: &str) -> Result<()>;
199
200    /// Select a text range (0-based character offsets).
201    fn set_text_selection(&self, element: &ElementData, start: u32, end: u32) -> Result<()>;
202
203    // ── Generic action escape hatch ─────────────────────────────────
204
205    /// Perform an action by `snake_case` name.
206    ///
207    /// This is the escape hatch for platform-specific actions not covered by
208    /// the first-class methods above. The provider converts the name to the
209    /// platform's convention (e.g. `"custom_thing"` → `"AXCustomThing"` on
210    /// macOS) and invokes it.
211    ///
212    /// Well-known action names (`"press"`, `"focus"`, etc.) should also work
213    /// here — providers should delegate to the corresponding method.
214    fn perform_action(&self, element: &ElementData, action: &str) -> Result<()>;
215
216    // ── Events ──────────────────────────────────────────────────────
217
218    /// Subscribe to all accessibility events for an application.
219    ///
220    /// The element should be an application-level element (role=Application).
221    /// The provider extracts the PID from `element.pid`.
222    ///
223    /// Returns a [`Subscription`] that receives events until dropped.
224    fn subscribe(&self, element: &ElementData) -> Result<Subscription>;
225}
226
227// Blanket impl so shared references to a provider act as providers themselves.
228// Used by the umbrella crate's singleton (a `&'static dyn Provider` wrapped in
229// `Arc<_>`) and by any caller that wants to share a provider via `&T`. The
230// orphan rules keep this collision-free for downstream crates because `xa11y-core`
231// owns the `Provider` trait.
232impl<T: Provider + ?Sized> Provider for &T {
233    fn get_children(&self, element: Option<&ElementData>) -> Result<Vec<ElementData>> {
234        (**self).get_children(element)
235    }
236    fn get_parent(&self, element: &ElementData) -> Result<Option<ElementData>> {
237        (**self).get_parent(element)
238    }
239    fn find_elements(
240        &self,
241        root: Option<&ElementData>,
242        selector: &Selector,
243        limit: Option<usize>,
244        max_depth: Option<u32>,
245    ) -> Result<Vec<ElementData>> {
246        (**self).find_elements(root, selector, limit, max_depth)
247    }
248    fn find_elements_group(
249        &self,
250        root: Option<&ElementData>,
251        group: &SelectorGroup,
252        limit: Option<usize>,
253        max_depth: Option<u32>,
254    ) -> Result<Vec<ElementData>> {
255        (**self).find_elements_group(root, group, limit, max_depth)
256    }
257    fn narrow_multi_segment(
258        &self,
259        candidates: Vec<ElementData>,
260        segments: &[SelectorSegment],
261        max_depth: u32,
262        limit: Option<usize>,
263    ) -> Result<Vec<ElementData>> {
264        (**self).narrow_multi_segment(candidates, segments, max_depth, limit)
265    }
266    fn press(&self, element: &ElementData) -> Result<()> {
267        (**self).press(element)
268    }
269    fn focus(&self, element: &ElementData) -> Result<()> {
270        (**self).focus(element)
271    }
272    fn blur(&self, element: &ElementData) -> Result<()> {
273        (**self).blur(element)
274    }
275    fn toggle(&self, element: &ElementData) -> Result<()> {
276        (**self).toggle(element)
277    }
278    fn select(&self, element: &ElementData) -> Result<()> {
279        (**self).select(element)
280    }
281    fn expand(&self, element: &ElementData) -> Result<()> {
282        (**self).expand(element)
283    }
284    fn collapse(&self, element: &ElementData) -> Result<()> {
285        (**self).collapse(element)
286    }
287    fn show_menu(&self, element: &ElementData) -> Result<()> {
288        (**self).show_menu(element)
289    }
290    fn increment(&self, element: &ElementData) -> Result<()> {
291        (**self).increment(element)
292    }
293    fn decrement(&self, element: &ElementData) -> Result<()> {
294        (**self).decrement(element)
295    }
296    fn scroll_into_view(&self, element: &ElementData) -> Result<()> {
297        (**self).scroll_into_view(element)
298    }
299    fn set_value(&self, element: &ElementData, value: &str) -> Result<()> {
300        (**self).set_value(element, value)
301    }
302    fn set_numeric_value(&self, element: &ElementData, value: f64) -> Result<()> {
303        (**self).set_numeric_value(element, value)
304    }
305    fn type_text(&self, element: &ElementData, text: &str) -> Result<()> {
306        (**self).type_text(element, text)
307    }
308    fn set_text_selection(&self, element: &ElementData, start: u32, end: u32) -> Result<()> {
309        (**self).set_text_selection(element, start, end)
310    }
311    fn perform_action(&self, element: &ElementData, action: &str) -> Result<()> {
312        (**self).perform_action(element, action)
313    }
314    fn subscribe(&self, element: &ElementData) -> Result<Subscription> {
315        (**self).subscribe(element)
316    }
317}