Skip to main content

xa11y_core/
provider.rs

1use crate::element::ElementData;
2use crate::error::Result;
3use crate::event_provider::Subscription;
4use crate::selector::Selector;
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    // ── Common actions ──────────────────────────────────────────────
61
62    /// Click / tap / invoke the element.
63    fn press(&self, element: &ElementData) -> Result<()>;
64
65    /// Set keyboard focus to the element.
66    fn focus(&self, element: &ElementData) -> Result<()>;
67
68    /// Remove keyboard focus from the element.
69    fn blur(&self, element: &ElementData) -> Result<()>;
70
71    /// Toggle a checkbox or switch.
72    fn toggle(&self, element: &ElementData) -> Result<()>;
73
74    /// Select an item in a list, tab group, or menu.
75    fn select(&self, element: &ElementData) -> Result<()>;
76
77    /// Expand a collapsible element (combo box, tree item, disclosure).
78    fn expand(&self, element: &ElementData) -> Result<()>;
79
80    /// Collapse an expanded element.
81    fn collapse(&self, element: &ElementData) -> Result<()>;
82
83    /// Show the element's context menu or dropdown.
84    fn show_menu(&self, element: &ElementData) -> Result<()>;
85
86    /// Increment a slider or spinner by one step.
87    fn increment(&self, element: &ElementData) -> Result<()>;
88
89    /// Decrement a slider or spinner by one step.
90    fn decrement(&self, element: &ElementData) -> Result<()>;
91
92    /// Scroll the element into the visible area.
93    fn scroll_into_view(&self, element: &ElementData) -> Result<()>;
94
95    // ── Typed operations ────────────────────────────────────────────
96
97    /// Set the text value of the element.
98    fn set_value(&self, element: &ElementData, value: &str) -> Result<()>;
99
100    /// Set the numeric value of the element (slider, spinner).
101    fn set_numeric_value(&self, element: &ElementData, value: f64) -> Result<()>;
102
103    /// Insert text at the current cursor position.
104    fn type_text(&self, element: &ElementData, text: &str) -> Result<()>;
105
106    /// Select a text range (0-based character offsets).
107    fn set_text_selection(&self, element: &ElementData, start: u32, end: u32) -> Result<()>;
108
109    /// Scroll in the given direction by the given amount.
110    ///
111    /// Amount is in logical scroll units (≈ one mouse wheel notch).
112    fn scroll_down(&self, element: &ElementData, amount: f64) -> Result<()>;
113    fn scroll_up(&self, element: &ElementData, amount: f64) -> Result<()>;
114    fn scroll_right(&self, element: &ElementData, amount: f64) -> Result<()>;
115    fn scroll_left(&self, element: &ElementData, amount: f64) -> Result<()>;
116
117    // ── Generic action escape hatch ─────────────────────────────────
118
119    /// Perform an action by `snake_case` name.
120    ///
121    /// This is the escape hatch for platform-specific actions not covered by
122    /// the first-class methods above. The provider converts the name to the
123    /// platform's convention (e.g. `"custom_thing"` → `"AXCustomThing"` on
124    /// macOS) and invokes it.
125    ///
126    /// Well-known action names (`"press"`, `"focus"`, etc.) should also work
127    /// here — providers should delegate to the corresponding method.
128    fn perform_action(&self, element: &ElementData, action: &str) -> Result<()>;
129
130    // ── Events ──────────────────────────────────────────────────────
131
132    /// Subscribe to all accessibility events for an application.
133    ///
134    /// The element should be an application-level element (role=Application).
135    /// The provider extracts the PID from `element.pid`.
136    ///
137    /// Returns a [`Subscription`] that receives events until dropped.
138    fn subscribe(&self, element: &ElementData) -> Result<Subscription>;
139}