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}