accesskit_atspi_common/
node.rs

1// Copyright 2022 The AccessKit Authors. All rights reserved.
2// Licensed under the Apache License, Version 2.0 (found in
3// the LICENSE-APACHE file) or the MIT license (found in
4// the LICENSE-MIT file), at your option.
5
6// Derived from Chromium's accessibility abstraction.
7// Copyright 2017 The Chromium Authors. All rights reserved.
8// Use of this source code is governed by a BSD-style license that can be
9// found in the LICENSE.chromium file.
10
11use accesskit::{
12    Action, ActionData, ActionRequest, Affine, Live, NodeId as LocalNodeId, Orientation, Point,
13    Rect, Role, Toggled, TreeId,
14};
15use accesskit_consumer::{FilterResult, Node, NodeId, Tree, TreeState};
16use atspi_common::{
17    CoordType, Granularity, Interface, InterfaceSet, Layer, Politeness, RelationType,
18    Role as AtspiRole, ScrollType, State, StateSet,
19};
20use std::{
21    collections::HashMap,
22    hash::{Hash, Hasher},
23    iter::FusedIterator,
24    sync::{Arc, RwLock, RwLockReadGuard, Weak},
25};
26
27use crate::{
28    adapter::Adapter,
29    context::{AppContext, Context},
30    filters::filter,
31    text_attributes::ATTRIBUTE_GETTERS,
32    util::*,
33    Action as AtspiAction, Error, ObjectEvent, Property, Rect as AtspiRect, Result,
34};
35
36pub(crate) struct NodeWrapper<'a>(pub(crate) &'a Node<'a>);
37
38impl NodeWrapper<'_> {
39    pub(crate) fn name(&self) -> Option<String> {
40        if self.0.label_comes_from_value() {
41            self.0.value()
42        } else {
43            self.0.label()
44        }
45    }
46
47    pub(crate) fn description(&self) -> Option<String> {
48        self.0.description()
49    }
50
51    pub(crate) fn parent_id(&self) -> Option<NodeId> {
52        self.0.parent_id()
53    }
54
55    pub(crate) fn id(&self) -> NodeId {
56        self.0.id()
57    }
58
59    fn filtered_child_ids(
60        &self,
61    ) -> impl DoubleEndedIterator<Item = NodeId> + FusedIterator<Item = NodeId> + '_ {
62        self.0.filtered_children(&filter).map(|child| child.id())
63    }
64
65    pub(crate) fn role(&self) -> AtspiRole {
66        if self.0.has_role_description() {
67            return AtspiRole::Extended;
68        }
69
70        match self.0.role() {
71            Role::Alert => AtspiRole::Notification,
72            Role::AlertDialog => AtspiRole::Alert,
73            Role::Comment | Role::Suggestion => AtspiRole::Section,
74            // TODO: See how to represent ARIA role="application"
75            Role::Application => AtspiRole::Embedded,
76            Role::Article => AtspiRole::Article,
77            Role::Audio => AtspiRole::Audio,
78            Role::Banner | Role::Header => AtspiRole::Landmark,
79            Role::Blockquote => AtspiRole::BlockQuote,
80            Role::Caret => AtspiRole::Unknown,
81            Role::Button => {
82                if self.0.toggled().is_some() {
83                    AtspiRole::ToggleButton
84                } else {
85                    AtspiRole::Button
86                }
87            }
88            Role::DefaultButton => AtspiRole::Button,
89            Role::Canvas => AtspiRole::Canvas,
90            Role::Caption => AtspiRole::Caption,
91            Role::Cell | Role::GridCell => AtspiRole::TableCell,
92            Role::CheckBox => AtspiRole::CheckBox,
93            Role::Switch => AtspiRole::ToggleButton,
94            Role::ColorWell => AtspiRole::Button,
95            Role::ColumnHeader => AtspiRole::ColumnHeader,
96            Role::ComboBox | Role::EditableComboBox => AtspiRole::ComboBox,
97            Role::Complementary => AtspiRole::Landmark,
98            Role::ContentDeletion => AtspiRole::ContentDeletion,
99            Role::ContentInsertion => AtspiRole::ContentInsertion,
100            Role::ContentInfo | Role::Footer => AtspiRole::Landmark,
101            Role::Definition => AtspiRole::DescriptionValue,
102            Role::DescriptionList => AtspiRole::DescriptionList,
103            Role::Details => AtspiRole::Panel,
104            Role::Dialog => AtspiRole::Dialog,
105            Role::DisclosureTriangle => AtspiRole::ToggleButton,
106            Role::DocCover => AtspiRole::Image,
107            Role::DocBackLink | Role::DocBiblioRef | Role::DocGlossRef | Role::DocNoteRef => {
108                AtspiRole::Link
109            }
110            Role::DocBiblioEntry | Role::DocEndnote => AtspiRole::ListItem,
111            Role::DocNotice | Role::DocTip => AtspiRole::Comment,
112            Role::DocFootnote => AtspiRole::Footnote,
113            Role::DocPageBreak => AtspiRole::Separator,
114            Role::DocPageFooter => AtspiRole::Footer,
115            Role::DocPageHeader => AtspiRole::Header,
116            Role::DocAcknowledgements
117            | Role::DocAfterword
118            | Role::DocAppendix
119            | Role::DocBibliography
120            | Role::DocChapter
121            | Role::DocConclusion
122            | Role::DocCredits
123            | Role::DocEndnotes
124            | Role::DocEpilogue
125            | Role::DocErrata
126            | Role::DocForeword
127            | Role::DocGlossary
128            | Role::DocIndex
129            | Role::DocIntroduction
130            | Role::DocPageList
131            | Role::DocPart
132            | Role::DocPreface
133            | Role::DocPrologue
134            | Role::DocToc => AtspiRole::Landmark,
135            Role::DocAbstract
136            | Role::DocColophon
137            | Role::DocCredit
138            | Role::DocDedication
139            | Role::DocEpigraph
140            | Role::DocExample
141            | Role::DocPullquote
142            | Role::DocQna => AtspiRole::Section,
143            Role::DocSubtitle => AtspiRole::Heading,
144            Role::Document => AtspiRole::DocumentFrame,
145            Role::EmbeddedObject => AtspiRole::Embedded,
146            // TODO: Forms which lack an accessible name are no longer
147            // exposed as forms. Forms which have accessible
148            // names should be exposed as `AtspiRole::Landmark` according to Core AAM.
149            Role::Form => AtspiRole::Form,
150            Role::Figure | Role::Feed => AtspiRole::Panel,
151            Role::GenericContainer | Role::Ruby => AtspiRole::Section,
152            Role::GraphicsDocument => AtspiRole::DocumentFrame,
153            Role::GraphicsObject => AtspiRole::Panel,
154            Role::GraphicsSymbol => AtspiRole::Image,
155            Role::Grid => AtspiRole::Table,
156            Role::Group => AtspiRole::Panel,
157            Role::Heading => AtspiRole::Heading,
158            Role::Iframe | Role::IframePresentational => AtspiRole::InternalFrame,
159            // TODO: If there are unignored children, then it should be AtspiRole::ImageMap.
160            Role::Image => AtspiRole::Image,
161            Role::TextRun => AtspiRole::Static,
162            Role::Legend => AtspiRole::Label,
163            // Layout table objects are treated the same as `Role::GenericContainer`.
164            Role::LayoutTable => AtspiRole::Section,
165            Role::LayoutTableCell => AtspiRole::Section,
166            Role::LayoutTableRow => AtspiRole::Section,
167            // TODO: Having a separate accessible object for line breaks
168            // is inconsistent with other implementations.
169            Role::LineBreak => AtspiRole::Static,
170            Role::Link => AtspiRole::Link,
171            Role::List => AtspiRole::List,
172            Role::ListBox => AtspiRole::ListBox,
173            // TODO: Use `AtspiRole::MenuItem' inside a combo box.
174            Role::ListBoxOption => AtspiRole::ListItem,
175            Role::ListGrid => AtspiRole::Table,
176            Role::ListItem => AtspiRole::ListItem,
177            // Regular list markers only expose their alternative text, but do not
178            // expose their descendants, and the descendants should be ignored. This
179            // is because the alternative text depends on the counter style and can
180            // be different from the actual (visual) marker text, and hence,
181            // inconsistent with the descendants. We treat a list marker as non-text
182            // only if it still has non-ignored descendants, which happens only when =>
183            // - The list marker itself is ignored but the descendants are not
184            // - Or the list marker contains images
185            Role::ListMarker => AtspiRole::Static,
186            Role::Log => AtspiRole::Log,
187            Role::Main => AtspiRole::Landmark,
188            Role::Mark => AtspiRole::Static,
189            Role::Math => AtspiRole::Math,
190            Role::Marquee => AtspiRole::Marquee,
191            Role::Menu | Role::MenuListPopup => AtspiRole::Menu,
192            Role::MenuBar => AtspiRole::MenuBar,
193            Role::MenuItem | Role::MenuListOption => AtspiRole::MenuItem,
194            Role::MenuItemCheckBox => AtspiRole::CheckMenuItem,
195            Role::MenuItemRadio => AtspiRole::RadioMenuItem,
196            Role::Meter => AtspiRole::LevelBar,
197            Role::Navigation => AtspiRole::Landmark,
198            Role::Note => AtspiRole::Comment,
199            Role::Pane | Role::ScrollView => AtspiRole::Panel,
200            Role::Paragraph => AtspiRole::Paragraph,
201            Role::PdfActionableHighlight => AtspiRole::Button,
202            Role::PdfRoot => AtspiRole::DocumentFrame,
203            Role::PluginObject => AtspiRole::Embedded,
204            Role::ProgressIndicator => AtspiRole::ProgressBar,
205            Role::RadioButton => AtspiRole::RadioButton,
206            Role::RadioGroup => AtspiRole::Panel,
207            Role::Region => AtspiRole::Landmark,
208            Role::RootWebArea => AtspiRole::DocumentWeb,
209            Role::Row => AtspiRole::TableRow,
210            Role::RowGroup => AtspiRole::Panel,
211            Role::RowHeader => AtspiRole::RowHeader,
212            // TODO: Generally exposed as description on <ruby> (`Role::Ruby`) element, not
213            // as its own object in the tree.
214            // However, it's possible to make a `Role::RubyAnnotation` element show up in the
215            // tree, for example by adding tabindex="0" to the source <rp> or <rt>
216            // element or making the source element the target of an aria-owns.
217            // Therefore, we need to gracefully handle it if it actually
218            // shows up in the tree.
219            Role::RubyAnnotation => AtspiRole::Static,
220            Role::Section => AtspiRole::Section,
221            Role::SectionFooter => AtspiRole::Footer,
222            Role::SectionHeader => AtspiRole::Header,
223            Role::ScrollBar => AtspiRole::ScrollBar,
224            Role::Search => AtspiRole::Landmark,
225            Role::Slider => AtspiRole::Slider,
226            Role::SpinButton => AtspiRole::SpinButton,
227            Role::Splitter => AtspiRole::Separator,
228            Role::Label => AtspiRole::Label,
229            Role::Status => AtspiRole::StatusBar,
230            Role::SvgRoot => AtspiRole::DocumentFrame,
231            Role::Tab => AtspiRole::PageTab,
232            Role::Table => AtspiRole::Table,
233            Role::TabList => AtspiRole::PageTabList,
234            Role::TabPanel => AtspiRole::ScrollPane,
235            // TODO: This mapping should also be applied to the dfn
236            // element.
237            Role::Term => AtspiRole::DescriptionTerm,
238            Role::TitleBar => AtspiRole::TitleBar,
239            Role::TextInput
240            | Role::MultilineTextInput
241            | Role::SearchInput
242            | Role::EmailInput
243            | Role::NumberInput
244            | Role::PhoneNumberInput
245            | Role::UrlInput => AtspiRole::Entry,
246            Role::DateInput
247            | Role::DateTimeInput
248            | Role::WeekInput
249            | Role::MonthInput
250            | Role::TimeInput => AtspiRole::DateEditor,
251            Role::PasswordInput => AtspiRole::PasswordText,
252            Role::Abbr | Role::Code | Role::Emphasis | Role::Strong | Role::Time => {
253                AtspiRole::Static
254            }
255            Role::Timer => AtspiRole::Timer,
256            Role::Toolbar => AtspiRole::ToolBar,
257            Role::Tooltip => AtspiRole::ToolTip,
258            Role::Tree => AtspiRole::Tree,
259            Role::TreeItem => AtspiRole::TreeItem,
260            Role::TreeGrid => AtspiRole::TreeTable,
261            Role::Video => AtspiRole::Video,
262            // In AT-SPI, elements with `AtspiRole::Frame` are windows with titles and
263            // buttons, while those with `AtspiRole::Window` are windows without those
264            // elements.
265            Role::Window => AtspiRole::Frame,
266            Role::WebView => AtspiRole::Panel,
267            Role::FigureCaption => AtspiRole::Caption,
268            // TODO: Are there special cases to consider?
269            Role::Unknown => AtspiRole::Unknown,
270            Role::ImeCandidate | Role::Keyboard => AtspiRole::RedundantObject,
271            Role::Terminal => AtspiRole::Terminal,
272        }
273    }
274
275    fn is_focused(&self) -> bool {
276        self.0.is_focused()
277    }
278
279    pub(crate) fn state(&self, is_window_focused: bool) -> StateSet {
280        let state = self.0;
281        let atspi_role = self.role();
282        let mut atspi_state = StateSet::empty();
283        if is_window_focused
284            && ((state.parent_id().is_none() && state.role() == Role::Window)
285                || (state.is_dialog()
286                    && state.tree_state.active_dialog().map(|d| d.id()) == Some(state.id())))
287        {
288            atspi_state.insert(State::Active);
289        }
290        if state.is_text_input() && !state.is_read_only() {
291            atspi_state.insert(State::Editable);
292        }
293        // TODO: Focus and selection.
294        if state.is_focusable(&filter) {
295            atspi_state.insert(State::Focusable);
296        }
297        let filter_result = filter(self.0);
298        if filter_result == FilterResult::Include {
299            atspi_state.insert(State::Visible | State::Showing);
300        }
301        if state.is_required() {
302            atspi_state.insert(State::Required);
303        }
304        if state.is_multiselectable() {
305            atspi_state.insert(State::Multiselectable);
306        }
307        if let Some(orientation) = state.orientation() {
308            atspi_state.insert(if orientation == Orientation::Horizontal {
309                State::Horizontal
310            } else {
311                State::Vertical
312            });
313        }
314        if atspi_role != AtspiRole::ToggleButton && state.toggled().is_some() {
315            atspi_state.insert(State::Checkable);
316        }
317        if state.is_modal() {
318            atspi_state.insert(State::Modal);
319        }
320        if let Some(selected) = state.is_selected() {
321            if !state.is_disabled() {
322                atspi_state.insert(State::Selectable);
323            }
324            if selected {
325                atspi_state.insert(State::Selected);
326            }
327        }
328        if state.is_text_input() {
329            atspi_state.insert(State::SelectableText);
330            atspi_state.insert(match state.is_multiline() {
331                true => State::MultiLine,
332                false => State::SingleLine,
333            });
334        }
335
336        // Special case for indeterminate progressbar.
337        if state.role() == Role::ProgressIndicator && state.numeric_value().is_none() {
338            atspi_state.insert(State::Indeterminate);
339        }
340
341        // Toggled state
342        match state.toggled() {
343            Some(Toggled::Mixed) => atspi_state.insert(State::Indeterminate),
344            Some(Toggled::True) if atspi_role == AtspiRole::ToggleButton => {
345                atspi_state.insert(State::Pressed)
346            }
347            Some(Toggled::True) => atspi_state.insert(State::Checked),
348            _ => {}
349        }
350
351        if state.is_read_only_supported() && state.is_read_only_or_disabled() {
352            atspi_state.insert(State::ReadOnly);
353        } else {
354            atspi_state.insert(State::Enabled | State::Sensitive);
355        }
356
357        if self.is_focused() {
358            atspi_state.insert(State::Focused);
359        }
360
361        atspi_state
362    }
363
364    fn placeholder(&self) -> Option<&str> {
365        self.0.placeholder()
366    }
367
368    fn position_in_set(&self) -> Option<String> {
369        self.0.position_in_set().map(|p| (p + 1).to_string())
370    }
371
372    fn size_of_set(&self) -> Option<String> {
373        self.0
374            .size_of_set_from_container(&filter)
375            .map(|s| s.to_string())
376    }
377
378    fn braille_label(&self) -> Option<&str> {
379        self.0.braille_label()
380    }
381
382    fn braille_role_description(&self) -> Option<&str> {
383        self.0.braille_role_description()
384    }
385
386    fn attributes(&self) -> HashMap<&'static str, String> {
387        let mut attributes = HashMap::new();
388        if let Some(placeholder) = self.placeholder() {
389            attributes.insert("placeholder-text", placeholder.to_string());
390        }
391        if let Some(position_in_set) = self.position_in_set() {
392            attributes.insert("posinset", position_in_set);
393        }
394        if let Some(size_of_set) = self.size_of_set() {
395            attributes.insert("setsize", size_of_set);
396        }
397        if let Some(label) = self.braille_label() {
398            attributes.insert("braillelabel", label.to_string());
399        }
400        if let Some(role_description) = self.braille_role_description() {
401            attributes.insert("brailleroledescription", role_description.to_string());
402        }
403
404        attributes
405    }
406
407    fn is_root(&self) -> bool {
408        self.0.is_root()
409    }
410
411    fn supports_action(&self) -> bool {
412        self.0.is_clickable(&filter)
413    }
414
415    fn supports_component(&self) -> bool {
416        self.0.raw_bounds().is_some() || self.is_root()
417    }
418
419    fn supports_hyperlink(&self) -> bool {
420        self.0.supports_url()
421    }
422
423    fn supports_selection(&self) -> bool {
424        self.0.is_container_with_selectable_children()
425    }
426
427    fn supports_text(&self) -> bool {
428        self.0.supports_text_ranges()
429    }
430
431    fn supports_value(&self) -> bool {
432        self.current_value().is_some()
433    }
434
435    pub(crate) fn interfaces(&self) -> InterfaceSet {
436        let mut interfaces = InterfaceSet::new(Interface::Accessible);
437        if self.supports_action() {
438            interfaces.insert(Interface::Action);
439        }
440        if self.supports_component() {
441            interfaces.insert(Interface::Component);
442        }
443        if self.supports_hyperlink() {
444            interfaces.insert(Interface::Hyperlink);
445        }
446        if self.supports_selection() {
447            interfaces.insert(Interface::Selection);
448        }
449        if self.supports_text() {
450            interfaces.insert(Interface::Text);
451        }
452        if self.supports_value() {
453            interfaces.insert(Interface::Value);
454        }
455        interfaces
456    }
457
458    pub(crate) fn live(&self) -> Politeness {
459        let live = self.0.live();
460        match live {
461            Live::Off => Politeness::None,
462            Live::Polite => Politeness::Polite,
463            Live::Assertive => Politeness::Assertive,
464        }
465    }
466
467    fn n_actions(&self) -> i32 {
468        if self.0.is_clickable(&filter) {
469            1
470        } else {
471            0
472        }
473    }
474
475    fn get_action_name(&self, index: i32) -> String {
476        if index != 0 {
477            return String::new();
478        }
479        String::from(if self.0.is_clickable(&filter) {
480            "click"
481        } else {
482            ""
483        })
484    }
485
486    fn raw_bounds_and_transform(&self) -> (Option<Rect>, Affine) {
487        let state = self.0;
488        (state.raw_bounds(), state.direct_transform())
489    }
490
491    fn extents(&self, window_bounds: &WindowBounds, coord_type: CoordType) -> Option<Rect> {
492        let mut bounds = self.0.bounding_box();
493        if self.is_root() {
494            let window_bounds = window_bounds.inner.with_origin(Point::ZERO);
495            if !window_bounds.is_empty() {
496                if let Some(bounds) = &mut bounds {
497                    bounds.x0 = bounds.x0.min(window_bounds.x1);
498                    bounds.y0 = bounds.y0.min(window_bounds.y1);
499                    bounds.x1 = bounds.x1.min(window_bounds.x1);
500                    bounds.y1 = bounds.y1.min(window_bounds.y1);
501                } else {
502                    bounds = Some(window_bounds);
503                }
504            }
505        }
506        bounds.map(|bounds| {
507            let new_origin = window_bounds.accesskit_point_to_atspi_point(
508                bounds.origin(),
509                self.0.filtered_parent(&filter),
510                coord_type,
511            );
512            bounds.with_origin(new_origin)
513        })
514    }
515
516    fn current_value(&self) -> Option<f64> {
517        self.0.numeric_value()
518    }
519
520    pub(crate) fn notify_changes(
521        &self,
522        window_bounds: &WindowBounds,
523        adapter: &Adapter,
524        old: &NodeWrapper<'_>,
525    ) {
526        self.notify_state_changes(adapter, old);
527        self.notify_property_changes(adapter, old);
528        self.notify_bounds_changes(window_bounds, adapter, old);
529        self.notify_children_changes(adapter, old);
530    }
531
532    fn notify_state_changes(&self, adapter: &Adapter, old: &NodeWrapper<'_>) {
533        let old_state = old.state(true);
534        let new_state = self.state(true);
535        let changed_states = old_state ^ new_state;
536        for state in changed_states.iter() {
537            if state == State::Focused {
538                // This is handled specially in `focus_moved`.
539                continue;
540            }
541            adapter.emit_object_event(
542                self.id(),
543                ObjectEvent::StateChanged(state, new_state.contains(state)),
544            );
545        }
546    }
547
548    fn notify_property_changes(&self, adapter: &Adapter, old: &NodeWrapper<'_>) {
549        let name = self.name();
550        if name != old.name() {
551            let name = name.unwrap_or_default();
552            adapter.emit_object_event(
553                self.id(),
554                ObjectEvent::PropertyChanged(Property::Name(name.clone())),
555            );
556
557            let live = self.live();
558            if live != Politeness::None {
559                adapter.emit_object_event(self.id(), ObjectEvent::Announcement(name, live));
560            }
561        }
562        let description = self.description();
563        if description != old.description() {
564            adapter.emit_object_event(
565                self.id(),
566                ObjectEvent::PropertyChanged(Property::Description(
567                    description.unwrap_or_default(),
568                )),
569            );
570        }
571        let parent_id = self.parent_id();
572        if parent_id != old.parent_id() {
573            let parent = self
574                .0
575                .filtered_parent(&filter)
576                .map_or(NodeIdOrRoot::Root, |node| NodeIdOrRoot::Node(node.id()));
577            adapter.emit_object_event(
578                self.id(),
579                ObjectEvent::PropertyChanged(Property::Parent(parent)),
580            );
581        }
582        let role = self.role();
583        if role != old.role() {
584            adapter.emit_object_event(
585                self.id(),
586                ObjectEvent::PropertyChanged(Property::Role(role)),
587            );
588        }
589        if let Some(value) = self.current_value() {
590            if Some(value) != old.current_value() {
591                adapter.emit_object_event(
592                    self.id(),
593                    ObjectEvent::PropertyChanged(Property::Value(value)),
594                );
595            }
596        }
597    }
598
599    fn notify_bounds_changes(
600        &self,
601        window_bounds: &WindowBounds,
602        adapter: &Adapter,
603        old: &NodeWrapper<'_>,
604    ) {
605        if self.raw_bounds_and_transform() != old.raw_bounds_and_transform() {
606            if let Some(extents) = self.extents(window_bounds, CoordType::Window) {
607                adapter.emit_object_event(self.id(), ObjectEvent::BoundsChanged(extents.into()));
608            }
609        }
610    }
611
612    fn notify_children_changes(&self, adapter: &Adapter, old: &NodeWrapper<'_>) {
613        let old_filtered_children = old.filtered_child_ids().collect::<Vec<NodeId>>();
614        let new_filtered_children = self.filtered_child_ids().collect::<Vec<NodeId>>();
615        for (index, child) in new_filtered_children.iter().enumerate() {
616            if !old_filtered_children.contains(child) {
617                adapter.emit_object_event(self.id(), ObjectEvent::ChildAdded(index, *child));
618            }
619        }
620        for child in old_filtered_children.into_iter() {
621            if !new_filtered_children.contains(&child) {
622                adapter.emit_object_event(self.id(), ObjectEvent::ChildRemoved(child));
623            }
624        }
625    }
626}
627
628#[derive(Clone)]
629pub struct PlatformNode {
630    context: Weak<Context>,
631    adapter_id: usize,
632    id: NodeId,
633}
634
635impl PlatformNode {
636    pub(crate) fn new(context: &Arc<Context>, adapter_id: usize, id: NodeId) -> Self {
637        Self {
638            context: Arc::downgrade(context),
639            adapter_id,
640            id,
641        }
642    }
643
644    fn from_adapter_root(adapter_id_and_context: &(usize, Arc<Context>)) -> Self {
645        let (adapter_id, context) = adapter_id_and_context;
646        Self::new(context, *adapter_id, context.read_tree().state().root_id())
647    }
648
649    fn upgrade_context(&self) -> Result<Arc<Context>> {
650        if let Some(context) = self.context.upgrade() {
651            Ok(context)
652        } else {
653            Err(Error::Defunct)
654        }
655    }
656
657    fn with_tree_state<F, T>(&self, f: F) -> Result<T>
658    where
659        F: FnOnce(&TreeState) -> Result<T>,
660    {
661        let context = self.upgrade_context()?;
662        let tree = context.read_tree();
663        f(tree.state())
664    }
665
666    fn with_tree_and_context<F, T>(&self, f: F) -> Result<T>
667    where
668        F: FnOnce(&Tree, &Context) -> Result<T>,
669    {
670        let context = self.upgrade_context()?;
671        let tree = context.read_tree();
672        f(&tree, &context)
673    }
674
675    fn resolve_with_context<F, T>(&self, f: F) -> Result<T>
676    where
677        for<'a> F: FnOnce(Node<'a>, &'a Tree, &Context) -> Result<T>,
678    {
679        self.with_tree_and_context(|tree, context| {
680            if let Some(node) = tree.state().node_by_id(self.id) {
681                f(node, tree, context)
682            } else {
683                Err(Error::Defunct)
684            }
685        })
686    }
687
688    fn resolve_for_selection_with_context<F, T>(&self, f: F) -> Result<T>
689    where
690        for<'a> F: FnOnce(Node<'a>, &'a Tree, &Context) -> Result<T>,
691    {
692        self.resolve_with_context(|node, tree, context| {
693            let wrapper = NodeWrapper(&node);
694            if wrapper.supports_selection() {
695                f(node, tree, context)
696            } else {
697                Err(Error::UnsupportedInterface)
698            }
699        })
700    }
701
702    fn resolve_for_text_with_context<F, T>(&self, f: F) -> Result<T>
703    where
704        for<'a> F: FnOnce(Node<'a>, &'a Tree, &Context) -> Result<T>,
705    {
706        self.resolve_with_context(|node, tree, context| {
707            let wrapper = NodeWrapper(&node);
708            if wrapper.supports_text() {
709                f(node, tree, context)
710            } else {
711                Err(Error::UnsupportedInterface)
712            }
713        })
714    }
715
716    fn resolve<F, T>(&self, f: F) -> Result<T>
717    where
718        for<'a> F: FnOnce(Node<'a>) -> Result<T>,
719    {
720        self.resolve_with_context(|node, _, _| f(node))
721    }
722
723    fn resolve_for_selection<F, T>(&self, f: F) -> Result<T>
724    where
725        for<'a> F: FnOnce(Node<'a>) -> Result<T>,
726    {
727        self.resolve(|node| {
728            let wrapper = NodeWrapper(&node);
729            if wrapper.supports_selection() {
730                f(node)
731            } else {
732                Err(Error::UnsupportedInterface)
733            }
734        })
735    }
736
737    fn resolve_for_text<F, T>(&self, f: F) -> Result<T>
738    where
739        for<'a> F: FnOnce(Node<'a>) -> Result<T>,
740    {
741        self.resolve_for_text_with_context(|node, _, _| f(node))
742    }
743
744    fn do_action_internal<F>(&self, target: NodeId, f: F) -> Result<()>
745    where
746        F: FnOnce(&TreeState, &Context, LocalNodeId, TreeId) -> ActionRequest,
747    {
748        let context = self.upgrade_context()?;
749        let tree = context.read_tree();
750        if let Some((target_node, target_tree)) = tree.state().locate_node(target) {
751            let request = f(tree.state(), &context, target_node, target_tree);
752            drop(tree);
753            context.do_action(request);
754            Ok(())
755        } else {
756            Err(Error::Defunct)
757        }
758    }
759
760    pub fn name(&self) -> Result<String> {
761        self.resolve(|node| {
762            let wrapper = NodeWrapper(&node);
763            Ok(wrapper.name().unwrap_or_default())
764        })
765    }
766
767    pub fn description(&self) -> Result<String> {
768        self.resolve(|node| {
769            let wrapper = NodeWrapper(&node);
770            Ok(wrapper.description().unwrap_or_default())
771        })
772    }
773
774    pub fn relative(&self, id: NodeId) -> Self {
775        Self {
776            context: self.context.clone(),
777            adapter_id: self.adapter_id,
778            id,
779        }
780    }
781
782    pub fn root(&self) -> Result<PlatformRoot> {
783        let context = self.upgrade_context()?;
784        Ok(PlatformRoot::new(&context.app_context))
785    }
786
787    pub fn toolkit_name(&self) -> Result<String> {
788        self.with_tree_state(|state| Ok(state.toolkit_name().unwrap_or_default().to_string()))
789    }
790
791    pub fn toolkit_version(&self) -> Result<String> {
792        self.with_tree_state(|state| Ok(state.toolkit_version().unwrap_or_default().to_string()))
793    }
794
795    pub fn parent(&self) -> Result<NodeIdOrRoot> {
796        self.resolve(|node| {
797            let parent = node
798                .filtered_parent(&filter)
799                .map_or(NodeIdOrRoot::Root, |node| NodeIdOrRoot::Node(node.id()));
800            Ok(parent)
801        })
802    }
803
804    pub fn child_count(&self) -> Result<i32> {
805        self.resolve(|node| {
806            i32::try_from(node.filtered_children(&filter).count())
807                .map_err(|_| Error::TooManyChildren)
808        })
809    }
810
811    pub fn adapter_id(&self) -> usize {
812        self.adapter_id
813    }
814
815    pub fn id(&self) -> NodeId {
816        self.id
817    }
818
819    pub fn accessible_id(&self) -> Result<String> {
820        self.resolve(|node| {
821            if let Some(author_id) = node.author_id() {
822                Ok(author_id.to_string())
823            } else {
824                Ok(String::new())
825            }
826        })
827    }
828
829    pub fn child_at_index(&self, index: usize) -> Result<Option<NodeId>> {
830        self.resolve(|node| {
831            let child = node
832                .filtered_children(&filter)
833                .nth(index)
834                .map(|child| child.id());
835            Ok(child)
836        })
837    }
838
839    pub fn map_children<T, I>(&self, f: impl Fn(NodeId) -> I) -> Result<T>
840    where
841        T: FromIterator<I>,
842    {
843        self.resolve(|node| {
844            let children = node
845                .filtered_children(&filter)
846                .map(|child| child.id())
847                .map(f)
848                .collect();
849            Ok(children)
850        })
851    }
852
853    pub fn index_in_parent(&self) -> Result<i32> {
854        self.resolve(|node| {
855            i32::try_from(node.preceding_filtered_siblings(&filter).count())
856                .map_err(|_| Error::IndexOutOfRange)
857        })
858    }
859
860    pub fn relation_set<T>(
861        &self,
862        f: impl Fn(NodeId) -> T,
863    ) -> Result<HashMap<RelationType, Vec<T>>> {
864        self.resolve(|node| {
865            let mut relations = HashMap::new();
866            let controls: Vec<_> = node
867                .controls()
868                .filter(|controlled| filter(controlled) == FilterResult::Include)
869                .map(|controlled| controlled.id())
870                .map(f)
871                .collect();
872            if !controls.is_empty() {
873                relations.insert(RelationType::ControllerFor, controls);
874            }
875            Ok(relations)
876        })
877    }
878
879    pub fn role(&self) -> Result<AtspiRole> {
880        self.resolve(|node| {
881            let wrapper = NodeWrapper(&node);
882            Ok(wrapper.role())
883        })
884    }
885
886    pub fn localized_role_name(&self) -> Result<String> {
887        self.resolve(|node| Ok(node.role_description().unwrap_or_default().to_string()))
888    }
889
890    pub fn state(&self) -> StateSet {
891        self.resolve_with_context(|node, tree, _| {
892            let wrapper = NodeWrapper(&node);
893            Ok(wrapper.state(tree.state().focus_id().is_some()))
894        })
895        .unwrap_or(State::Defunct.into())
896    }
897
898    pub fn attributes(&self) -> Result<HashMap<&'static str, String>> {
899        self.resolve(|node| {
900            let wrapper = NodeWrapper(&node);
901            Ok(wrapper.attributes())
902        })
903    }
904
905    pub fn supports_action(&self) -> Result<bool> {
906        self.resolve(|node| {
907            let wrapper = NodeWrapper(&node);
908            Ok(wrapper.supports_action())
909        })
910    }
911
912    pub fn supports_component(&self) -> Result<bool> {
913        self.resolve(|node| {
914            let wrapper = NodeWrapper(&node);
915            Ok(wrapper.supports_component())
916        })
917    }
918
919    pub fn supports_hyperlink(&self) -> Result<bool> {
920        self.resolve(|node| {
921            let wrapper = NodeWrapper(&node);
922            Ok(wrapper.supports_hyperlink())
923        })
924    }
925
926    pub fn supports_selection(&self) -> Result<bool> {
927        self.resolve(|node| {
928            let wrapper = NodeWrapper(&node);
929            Ok(wrapper.supports_selection())
930        })
931    }
932
933    pub fn supports_text(&self) -> Result<bool> {
934        self.resolve(|node| {
935            let wrapper = NodeWrapper(&node);
936            Ok(wrapper.supports_text())
937        })
938    }
939
940    pub fn supports_value(&self) -> Result<bool> {
941        self.resolve(|node| {
942            let wrapper = NodeWrapper(&node);
943            Ok(wrapper.supports_value())
944        })
945    }
946
947    pub fn interfaces(&self) -> Result<InterfaceSet> {
948        self.resolve(|node| {
949            let wrapper = NodeWrapper(&node);
950            Ok(wrapper.interfaces())
951        })
952    }
953
954    pub fn n_actions(&self) -> Result<i32> {
955        self.resolve(|node| {
956            let wrapper = NodeWrapper(&node);
957            Ok(wrapper.n_actions())
958        })
959    }
960
961    pub fn action_name(&self, index: i32) -> Result<String> {
962        self.resolve(|node| {
963            let wrapper = NodeWrapper(&node);
964            Ok(wrapper.get_action_name(index))
965        })
966    }
967
968    pub fn actions(&self) -> Result<Vec<AtspiAction>> {
969        self.resolve(|node| {
970            let wrapper = NodeWrapper(&node);
971            let n_actions = wrapper.n_actions() as usize;
972            let mut actions = Vec::with_capacity(n_actions);
973            for i in 0..n_actions {
974                actions.push(AtspiAction {
975                    localized_name: wrapper.get_action_name(i as i32),
976                    description: "".into(),
977                    key_binding: "".into(),
978                });
979            }
980            Ok(actions)
981        })
982    }
983
984    pub fn do_action(&self, index: i32) -> Result<bool> {
985        if index != 0 {
986            return Ok(false);
987        }
988        self.do_action_internal(self.id, |_, _, target_node, target_tree| ActionRequest {
989            action: Action::Click,
990            target_tree,
991            target_node,
992            data: None,
993        })?;
994        Ok(true)
995    }
996
997    pub fn contains(&self, x: i32, y: i32, coord_type: CoordType) -> Result<bool> {
998        self.resolve_with_context(|node, _, context| {
999            let window_bounds = context.read_root_window_bounds();
1000            let wrapper = NodeWrapper(&node);
1001            if let Some(extents) = wrapper.extents(&window_bounds, coord_type) {
1002                Ok(extents.contains(Point::new(x.into(), y.into())))
1003            } else {
1004                Ok(false)
1005            }
1006        })
1007    }
1008
1009    pub fn accessible_at_point(
1010        &self,
1011        x: i32,
1012        y: i32,
1013        coord_type: CoordType,
1014    ) -> Result<Option<NodeId>> {
1015        self.resolve_with_context(|node, _, context| {
1016            let window_bounds = context.read_root_window_bounds();
1017            let point = window_bounds.atspi_point_to_accesskit_point(
1018                Point::new(x.into(), y.into()),
1019                Some(node),
1020                coord_type,
1021            );
1022            let point = node.transform().inverse() * point;
1023            Ok(node.node_at_point(point, &filter).map(|node| node.id()))
1024        })
1025    }
1026
1027    pub fn extents(&self, coord_type: CoordType) -> Result<AtspiRect> {
1028        self.resolve_with_context(|node, _, context| {
1029            let window_bounds = context.read_root_window_bounds();
1030            let wrapper = NodeWrapper(&node);
1031            Ok(wrapper
1032                .extents(&window_bounds, coord_type)
1033                .map_or(AtspiRect::INVALID, AtspiRect::from))
1034        })
1035    }
1036
1037    pub fn layer(&self) -> Result<Layer> {
1038        self.resolve(|node| {
1039            let wrapper = NodeWrapper(&node);
1040            if wrapper.role() == AtspiRole::Window {
1041                Ok(Layer::Window)
1042            } else {
1043                Ok(Layer::Widget)
1044            }
1045        })
1046    }
1047
1048    pub fn grab_focus(&self) -> Result<bool> {
1049        self.do_action_internal(self.id, |_, _, target_node, target_tree| ActionRequest {
1050            action: Action::Focus,
1051            target_tree,
1052            target_node,
1053            data: None,
1054        })?;
1055        Ok(true)
1056    }
1057
1058    pub fn scroll_to(&self, scroll_type: ScrollType) -> Result<bool> {
1059        self.do_action_internal(self.id, |_, _, target_node, target_tree| ActionRequest {
1060            action: Action::ScrollIntoView,
1061            target_tree,
1062            target_node,
1063            data: atspi_scroll_type_to_scroll_hint(scroll_type).map(ActionData::ScrollHint),
1064        })?;
1065        Ok(true)
1066    }
1067
1068    pub fn scroll_to_point(&self, coord_type: CoordType, x: i32, y: i32) -> Result<bool> {
1069        self.resolve_with_context(|node, tree, context| {
1070            let window_bounds = context.read_root_window_bounds();
1071            let point = window_bounds.atspi_point_to_accesskit_point(
1072                Point::new(x.into(), y.into()),
1073                node.filtered_parent(&filter),
1074                coord_type,
1075            );
1076            let (target_node, target_tree) =
1077                tree.state().locate_node(self.id).ok_or(Error::Defunct)?;
1078            context.do_action(ActionRequest {
1079                action: Action::ScrollToPoint,
1080                target_tree,
1081                target_node,
1082                data: Some(ActionData::ScrollToPoint(point)),
1083            });
1084            Ok(())
1085        })?;
1086        Ok(true)
1087    }
1088
1089    pub fn n_anchors(&self) -> Result<i32> {
1090        self.resolve(|node| if node.url().is_some() { Ok(1) } else { Ok(0) })
1091    }
1092
1093    pub fn hyperlink_start_index(&self) -> Result<i32> {
1094        self.resolve(|_| {
1095            // TODO: Support rich text
1096            Ok(-1)
1097        })
1098    }
1099
1100    pub fn hyperlink_end_index(&self) -> Result<i32> {
1101        self.resolve(|_| {
1102            // TODO: Support rich text
1103            Ok(-1)
1104        })
1105    }
1106
1107    pub fn hyperlink_object(&self, index: i32) -> Result<Option<NodeId>> {
1108        self.resolve(|_| {
1109            if index == 0 {
1110                Ok(Some(self.id))
1111            } else {
1112                Ok(None)
1113            }
1114        })
1115    }
1116
1117    pub fn uri(&self, index: i32) -> Result<String> {
1118        self.resolve(|node| {
1119            if index == 0 {
1120                Ok(node.url().map(|s| s.to_string()).unwrap_or_default())
1121            } else {
1122                Ok(String::new())
1123            }
1124        })
1125    }
1126
1127    pub fn hyperlink_is_valid(&self) -> Result<bool> {
1128        self.resolve(|node| Ok(node.url().is_some()))
1129    }
1130
1131    pub fn n_selected_children(&self) -> Result<i32> {
1132        self.resolve_for_selection(|node| {
1133            node.items(filter)
1134                .filter(|item| item.is_selected() == Some(true))
1135                .count()
1136                .try_into()
1137                .map_err(|_| Error::TooManyChildren)
1138        })
1139    }
1140
1141    pub fn selected_child(&self, selected_child_index: usize) -> Result<Option<NodeId>> {
1142        self.resolve_for_selection(|node| {
1143            Ok(node
1144                .items(filter)
1145                .filter(|item| item.is_selected() == Some(true))
1146                .nth(selected_child_index)
1147                .map(|node| node.id()))
1148        })
1149    }
1150
1151    pub fn select_child(&self, child_index: usize) -> Result<bool> {
1152        self.resolve_for_selection_with_context(|node, tree, context| {
1153            if let Some(child) = node.filtered_children(filter).nth(child_index) {
1154                if let Some(true) = child.is_selected() {
1155                    Ok(true)
1156                } else if child.is_selectable() && child.is_clickable(&filter) {
1157                    let (target_node, target_tree) =
1158                        tree.state().locate_node(child.id()).ok_or(Error::Defunct)?;
1159                    context.do_action(ActionRequest {
1160                        action: Action::Click,
1161                        target_tree,
1162                        target_node,
1163                        data: None,
1164                    });
1165                    Ok(true)
1166                } else {
1167                    Ok(false)
1168                }
1169            } else {
1170                Err(Error::Defunct)
1171            }
1172        })
1173    }
1174
1175    pub fn deselect_selected_child(&self, selected_child_index: usize) -> Result<bool> {
1176        self.resolve_for_selection_with_context(|node, tree, context| {
1177            if let Some(child) = node
1178                .items(filter)
1179                .filter(|c| c.is_selected() == Some(true))
1180                .nth(selected_child_index)
1181            {
1182                if child.is_clickable(&filter) {
1183                    let (target_node, target_tree) =
1184                        tree.state().locate_node(child.id()).ok_or(Error::Defunct)?;
1185                    context.do_action(ActionRequest {
1186                        action: Action::Click,
1187                        target_tree,
1188                        target_node,
1189                        data: None,
1190                    });
1191                    Ok(true)
1192                } else {
1193                    Ok(false)
1194                }
1195            } else {
1196                Err(Error::Defunct)
1197            }
1198        })
1199    }
1200
1201    pub fn is_child_selected(&self, child_index: usize) -> Result<bool> {
1202        self.resolve_for_selection(|node| {
1203            node.filtered_children(filter)
1204                .nth(child_index)
1205                .map(|child| child.is_item_like() && child.is_selected() == Some(true))
1206                .ok_or(Error::Defunct)
1207        })
1208    }
1209
1210    pub fn select_all(&self) -> Result<bool> {
1211        // We don't support selecting all children at once.
1212        Ok(false)
1213    }
1214
1215    pub fn clear_selection(&self) -> Result<bool> {
1216        // We don't support deselecting all children at once.
1217        Ok(false)
1218    }
1219
1220    pub fn deselect_child(&self, child_index: usize) -> Result<bool> {
1221        self.resolve_for_selection_with_context(|node, tree, context| {
1222            if let Some(child) = node.filtered_children(filter).nth(child_index) {
1223                if let Some(false) = child.is_selected() {
1224                    Ok(true)
1225                } else if child.is_selectable() && child.is_clickable(&filter) {
1226                    let (target_node, target_tree) =
1227                        tree.state().locate_node(child.id()).ok_or(Error::Defunct)?;
1228                    context.do_action(ActionRequest {
1229                        action: Action::Click,
1230                        target_tree,
1231                        target_node,
1232                        data: None,
1233                    });
1234                    Ok(true)
1235                } else {
1236                    Ok(false)
1237                }
1238            } else {
1239                Err(Error::Defunct)
1240            }
1241        })
1242    }
1243
1244    pub fn character_count(&self) -> Result<i32> {
1245        self.resolve_for_text(|node| {
1246            node.document_range()
1247                .end()
1248                .to_global_usv_index()
1249                .try_into()
1250                .map_err(|_| Error::TooManyCharacters)
1251        })
1252    }
1253
1254    pub fn caret_offset(&self) -> Result<i32> {
1255        self.resolve_for_text(|node| {
1256            node.text_selection_focus().map_or(Ok(-1), |focus| {
1257                focus
1258                    .to_global_usv_index()
1259                    .try_into()
1260                    .map_err(|_| Error::TooManyCharacters)
1261            })
1262        })
1263    }
1264
1265    pub fn string_at_offset(
1266        &self,
1267        offset: i32,
1268        granularity: Granularity,
1269    ) -> Result<(String, i32, i32)> {
1270        self.resolve_for_text(|node| {
1271            let range = text_range_from_offset(&node, offset, granularity)?;
1272            let text = range.text();
1273            let start = range
1274                .start()
1275                .to_global_usv_index()
1276                .try_into()
1277                .map_err(|_| Error::TooManyCharacters)?;
1278            let end = range
1279                .end()
1280                .to_global_usv_index()
1281                .try_into()
1282                .map_err(|_| Error::TooManyCharacters)?;
1283
1284            Ok((text, start, end))
1285        })
1286    }
1287
1288    pub fn text(&self, start_offset: i32, end_offset: i32) -> Result<String> {
1289        self.resolve_for_text(|node| {
1290            let range = text_range_from_offsets(&node, start_offset, end_offset)
1291                .ok_or(Error::IndexOutOfRange)?;
1292            Ok(range.text())
1293        })
1294    }
1295
1296    pub fn set_caret_offset(&self, offset: i32) -> Result<bool> {
1297        self.resolve_for_text_with_context(|node, tree, context| {
1298            let offset = text_position_from_offset(&node, offset).ok_or(Error::IndexOutOfRange)?;
1299            let (target_node, target_tree) =
1300                tree.state().locate_node(node.id()).ok_or(Error::Defunct)?;
1301            context.do_action(ActionRequest {
1302                action: Action::SetTextSelection,
1303                target_tree,
1304                target_node,
1305                data: Some(ActionData::SetTextSelection(
1306                    offset.to_degenerate_range().to_text_selection(),
1307                )),
1308            });
1309            Ok(true)
1310        })
1311    }
1312
1313    pub fn text_attribute_value(&self, offset: i32, attribute_name: &str) -> Result<String> {
1314        self.resolve_for_text(|node| {
1315            let pos = text_position_from_offset(&node, offset).ok_or(Error::IndexOutOfRange)?;
1316            Ok(ATTRIBUTE_GETTERS
1317                .get(attribute_name)
1318                .and_then(|getter| (*getter)(pos.inner_node()))
1319                .unwrap_or_default())
1320        })
1321    }
1322
1323    pub fn text_attributes(
1324        &self,
1325        offset: i32,
1326    ) -> Result<(HashMap<&'static str, String>, i32, i32)> {
1327        self.text_attribute_run(offset, false)
1328    }
1329
1330    pub fn default_text_attributes(&self) -> Result<HashMap<&'static str, String>> {
1331        self.resolve_for_text(|node| {
1332            let mut result = HashMap::with_capacity(ATTRIBUTE_GETTERS.len());
1333            for (name, getter) in ATTRIBUTE_GETTERS.entries() {
1334                if let Some(value) = (*getter)(&node) {
1335                    result.insert(*name, value);
1336                }
1337            }
1338            Ok(result)
1339        })
1340    }
1341
1342    pub fn character_extents(&self, offset: i32, coord_type: CoordType) -> Result<AtspiRect> {
1343        self.resolve_for_text_with_context(|node, _, context| {
1344            let range = text_range_from_offset(&node, offset, Granularity::Char)?;
1345            if let Some(bounds) = range.bounding_boxes().first() {
1346                let window_bounds = context.read_root_window_bounds();
1347                let new_origin = window_bounds.accesskit_point_to_atspi_point(
1348                    bounds.origin(),
1349                    Some(node),
1350                    coord_type,
1351                );
1352                Ok(bounds.with_origin(new_origin).into())
1353            } else {
1354                Ok(AtspiRect::INVALID)
1355            }
1356        })
1357    }
1358
1359    pub fn offset_at_point(&self, x: i32, y: i32, coord_type: CoordType) -> Result<i32> {
1360        self.resolve_for_text_with_context(|node, _, context| {
1361            let window_bounds = context.read_root_window_bounds();
1362            let point = window_bounds.atspi_point_to_accesskit_point(
1363                Point::new(x.into(), y.into()),
1364                Some(node),
1365                coord_type,
1366            );
1367            let point = node.transform().inverse() * point;
1368            node.text_position_at_point(point)
1369                .to_global_usv_index()
1370                .try_into()
1371                .map_err(|_| Error::TooManyCharacters)
1372        })
1373    }
1374
1375    pub fn n_selections(&self) -> Result<i32> {
1376        self.resolve_for_text(|node| {
1377            match node.text_selection().filter(|range| !range.is_degenerate()) {
1378                Some(_) => Ok(1),
1379                None => Ok(0),
1380            }
1381        })
1382    }
1383
1384    pub fn selection(&self, selection_num: i32) -> Result<(i32, i32)> {
1385        if selection_num != 0 {
1386            return Ok((-1, -1));
1387        }
1388
1389        self.resolve_for_text(|node| {
1390            node.text_selection()
1391                .filter(|range| !range.is_degenerate())
1392                .map_or(Ok((-1, -1)), |range| {
1393                    let start = range
1394                        .start()
1395                        .to_global_usv_index()
1396                        .try_into()
1397                        .map_err(|_| Error::TooManyCharacters)?;
1398                    let end = range
1399                        .end()
1400                        .to_global_usv_index()
1401                        .try_into()
1402                        .map_err(|_| Error::TooManyCharacters)?;
1403
1404                    Ok((start, end))
1405                })
1406        })
1407    }
1408
1409    pub fn add_selection(&self, start_offset: i32, end_offset: i32) -> Result<bool> {
1410        // We only support one selection.
1411        self.set_selection(0, start_offset, end_offset)
1412    }
1413
1414    pub fn remove_selection(&self, selection_num: i32) -> Result<bool> {
1415        if selection_num != 0 {
1416            return Ok(false);
1417        }
1418
1419        self.resolve_for_text_with_context(|node, tree, context| {
1420            // Simply collapse the selection to the position of the caret if a caret is
1421            // visible, otherwise set the selection to 0.
1422            let selection_end = node
1423                .text_selection_focus()
1424                .unwrap_or_else(|| node.document_range().start());
1425            let (target_node, target_tree) =
1426                tree.state().locate_node(node.id()).ok_or(Error::Defunct)?;
1427            context.do_action(ActionRequest {
1428                action: Action::SetTextSelection,
1429                target_tree,
1430                target_node,
1431                data: Some(ActionData::SetTextSelection(
1432                    selection_end.to_degenerate_range().to_text_selection(),
1433                )),
1434            });
1435            Ok(true)
1436        })
1437    }
1438
1439    pub fn set_selection(
1440        &self,
1441        selection_num: i32,
1442        start_offset: i32,
1443        end_offset: i32,
1444    ) -> Result<bool> {
1445        if selection_num != 0 {
1446            return Ok(false);
1447        }
1448
1449        self.resolve_for_text_with_context(|node, tree, context| {
1450            let range = text_range_from_offsets(&node, start_offset, end_offset)
1451                .ok_or(Error::IndexOutOfRange)?;
1452            let (target_node, target_tree) =
1453                tree.state().locate_node(node.id()).ok_or(Error::Defunct)?;
1454            context.do_action(ActionRequest {
1455                action: Action::SetTextSelection,
1456                target_tree,
1457                target_node,
1458                data: Some(ActionData::SetTextSelection(range.to_text_selection())),
1459            });
1460            Ok(true)
1461        })
1462    }
1463
1464    pub fn range_extents(
1465        &self,
1466        start_offset: i32,
1467        end_offset: i32,
1468        coord_type: CoordType,
1469    ) -> Result<AtspiRect> {
1470        self.resolve_for_text_with_context(|node, _, context| {
1471            if let Some(rect) = text_range_bounds_from_offsets(&node, start_offset, end_offset) {
1472                let window_bounds = context.read_root_window_bounds();
1473                let new_origin = window_bounds.accesskit_point_to_atspi_point(
1474                    rect.origin(),
1475                    Some(node),
1476                    coord_type,
1477                );
1478                Ok(rect.with_origin(new_origin).into())
1479            } else {
1480                Ok(AtspiRect::INVALID)
1481            }
1482        })
1483    }
1484
1485    pub fn text_attribute_run(
1486        &self,
1487        offset: i32,
1488        include_defaults: bool,
1489    ) -> Result<(HashMap<&'static str, String>, i32, i32)> {
1490        self.resolve_for_text(|node| {
1491            let pos = text_position_from_offset(&node, offset).ok_or(Error::IndexOutOfRange)?;
1492            let mut result = HashMap::with_capacity(ATTRIBUTE_GETTERS.len());
1493            for (name, getter) in ATTRIBUTE_GETTERS.entries() {
1494                if let Some(value) = (*getter)(pos.inner_node()) {
1495                    if !include_defaults {
1496                        if let Some(default) = (*getter)(&node) {
1497                            if value == default {
1498                                continue;
1499                            }
1500                        }
1501                    }
1502                    result.insert(*name, value);
1503                }
1504            }
1505            let start = if pos.is_format_start() {
1506                pos
1507            } else {
1508                pos.backward_to_format_start()
1509            };
1510            let end = pos.forward_to_format_end();
1511            Ok((
1512                result,
1513                start
1514                    .to_global_usv_index()
1515                    .try_into()
1516                    .map_err(|_| Error::TooManyCharacters)?,
1517                end.to_global_usv_index()
1518                    .try_into()
1519                    .map_err(|_| Error::TooManyCharacters)?,
1520            ))
1521        })
1522    }
1523
1524    pub fn scroll_substring_to(
1525        &self,
1526        start_offset: i32,
1527        end_offset: i32,
1528        scroll_type: ScrollType,
1529    ) -> Result<bool> {
1530        self.resolve_for_text_with_context(|node, tree, context| {
1531            if let Some(range) = text_range_from_offsets(&node, start_offset, end_offset) {
1532                let position = if matches!(
1533                    scroll_type,
1534                    ScrollType::BottomRight | ScrollType::BottomEdge | ScrollType::RightEdge
1535                ) {
1536                    range.end()
1537                } else {
1538                    range.start()
1539                };
1540                let (target_node, target_tree) = tree
1541                    .state()
1542                    .locate_node(position.inner_node().id())
1543                    .ok_or(Error::Defunct)?;
1544                context.do_action(ActionRequest {
1545                    action: Action::ScrollIntoView,
1546                    target_tree,
1547                    target_node,
1548                    data: atspi_scroll_type_to_scroll_hint(scroll_type).map(ActionData::ScrollHint),
1549                });
1550                Ok(true)
1551            } else {
1552                Ok(false)
1553            }
1554        })
1555    }
1556
1557    pub fn scroll_substring_to_point(
1558        &self,
1559        start_offset: i32,
1560        end_offset: i32,
1561        coord_type: CoordType,
1562        x: i32,
1563        y: i32,
1564    ) -> Result<bool> {
1565        self.resolve_for_text_with_context(|node, tree, context| {
1566            let window_bounds = context.read_root_window_bounds();
1567            let target_point = window_bounds.atspi_point_to_accesskit_point(
1568                Point::new(x.into(), y.into()),
1569                Some(node),
1570                coord_type,
1571            );
1572
1573            if let Some(rect) = text_range_bounds_from_offsets(&node, start_offset, end_offset) {
1574                let point = Point::new(target_point.x - rect.x0, target_point.y - rect.y0);
1575                let (target_node, target_tree) =
1576                    tree.state().locate_node(node.id()).ok_or(Error::Defunct)?;
1577                context.do_action(ActionRequest {
1578                    action: Action::ScrollToPoint,
1579                    target_tree,
1580                    target_node,
1581                    data: Some(ActionData::ScrollToPoint(point)),
1582                });
1583                return Ok(true);
1584            }
1585            Ok(false)
1586        })
1587    }
1588
1589    pub fn minimum_value(&self) -> Result<f64> {
1590        self.resolve(|node| Ok(node.min_numeric_value().unwrap_or(f64::MIN)))
1591    }
1592
1593    pub fn maximum_value(&self) -> Result<f64> {
1594        self.resolve(|node| Ok(node.max_numeric_value().unwrap_or(f64::MAX)))
1595    }
1596
1597    pub fn minimum_increment(&self) -> Result<f64> {
1598        self.resolve(|node| Ok(node.numeric_value_step().unwrap_or(0.0)))
1599    }
1600
1601    pub fn current_value(&self) -> Result<f64> {
1602        self.resolve(|node| {
1603            let wrapper = NodeWrapper(&node);
1604            Ok(wrapper.current_value().unwrap_or(0.0))
1605        })
1606    }
1607
1608    pub fn set_current_value(&self, value: f64) -> Result<()> {
1609        self.do_action_internal(self.id, |_, _, target_node, target_tree| ActionRequest {
1610            action: Action::SetValue,
1611            target_tree,
1612            target_node,
1613            data: Some(ActionData::NumericValue(value)),
1614        })
1615    }
1616}
1617
1618impl PartialEq for PlatformNode {
1619    fn eq(&self, other: &Self) -> bool {
1620        self.adapter_id == other.adapter_id && self.id == other.id
1621    }
1622}
1623
1624impl Eq for PlatformNode {}
1625
1626impl Hash for PlatformNode {
1627    fn hash<H: Hasher>(&self, state: &mut H) {
1628        self.adapter_id.hash(state);
1629        self.id.hash(state);
1630    }
1631}
1632
1633#[derive(Clone)]
1634pub struct PlatformRoot {
1635    app_context: Weak<RwLock<AppContext>>,
1636}
1637
1638impl PlatformRoot {
1639    pub fn new(app_context: &Arc<RwLock<AppContext>>) -> Self {
1640        Self {
1641            app_context: Arc::downgrade(app_context),
1642        }
1643    }
1644
1645    fn resolve_app_context<F, T>(&self, f: F) -> Result<T>
1646    where
1647        for<'a> F: FnOnce(RwLockReadGuard<'a, AppContext>) -> Result<T>,
1648    {
1649        let app_context = match self.app_context.upgrade() {
1650            Some(context) => context,
1651            None => return Err(Error::Defunct),
1652        };
1653        let app_context = app_context.read().unwrap();
1654        f(app_context)
1655    }
1656
1657    pub fn name(&self) -> Result<String> {
1658        self.resolve_app_context(|context| Ok(context.name.clone().unwrap_or_default()))
1659    }
1660
1661    pub fn child_count(&self) -> Result<i32> {
1662        self.resolve_app_context(|context| {
1663            i32::try_from(context.adapters.len()).map_err(|_| Error::TooManyChildren)
1664        })
1665    }
1666
1667    pub fn child_at_index(&self, index: usize) -> Result<Option<PlatformNode>> {
1668        self.resolve_app_context(|context| {
1669            let child = context
1670                .adapters
1671                .get(index)
1672                .map(PlatformNode::from_adapter_root);
1673            Ok(child)
1674        })
1675    }
1676
1677    pub fn child_id_at_index(&self, index: usize) -> Result<Option<(usize, NodeId)>> {
1678        self.resolve_app_context(|context| {
1679            let child = context
1680                .adapters
1681                .get(index)
1682                .map(|(adapter_id, context)| (*adapter_id, context.read_tree().state().root_id()));
1683            Ok(child)
1684        })
1685    }
1686
1687    pub fn map_children<T, I>(&self, f: impl Fn(PlatformNode) -> I) -> Result<T>
1688    where
1689        T: FromIterator<I>,
1690    {
1691        self.resolve_app_context(|context| {
1692            let children = context
1693                .adapters
1694                .iter()
1695                .map(PlatformNode::from_adapter_root)
1696                .map(f)
1697                .collect();
1698            Ok(children)
1699        })
1700    }
1701
1702    pub fn map_child_ids<T, I>(&self, f: impl Fn((usize, NodeId)) -> I) -> Result<T>
1703    where
1704        T: FromIterator<I>,
1705    {
1706        self.resolve_app_context(|context| {
1707            let children = context
1708                .adapters
1709                .iter()
1710                .map(|(adapter_id, context)| (*adapter_id, context.read_tree().state().root_id()))
1711                .map(f)
1712                .collect();
1713            Ok(children)
1714        })
1715    }
1716
1717    pub fn toolkit_name(&self) -> Result<String> {
1718        self.resolve_app_context(|context| Ok(context.toolkit_name.clone().unwrap_or_default()))
1719    }
1720
1721    pub fn toolkit_version(&self) -> Result<String> {
1722        self.resolve_app_context(|context| Ok(context.toolkit_version.clone().unwrap_or_default()))
1723    }
1724
1725    pub fn id(&self) -> Result<i32> {
1726        self.resolve_app_context(|context| Ok(context.id.unwrap_or(-1)))
1727    }
1728
1729    pub fn set_id(&mut self, id: i32) -> Result<()> {
1730        let app_context = match self.app_context.upgrade() {
1731            Some(context) => context,
1732            None => return Err(Error::Defunct),
1733        };
1734        let mut app_context = app_context.write().unwrap();
1735        app_context.id = Some(id);
1736        Ok(())
1737    }
1738}
1739
1740impl PartialEq for PlatformRoot {
1741    fn eq(&self, other: &Self) -> bool {
1742        self.app_context.ptr_eq(&other.app_context)
1743    }
1744}
1745
1746impl Hash for PlatformRoot {
1747    fn hash<H: Hasher>(&self, state: &mut H) {
1748        self.app_context.as_ptr().hash(state);
1749    }
1750}
1751
1752#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1753pub enum NodeIdOrRoot {
1754    Node(NodeId),
1755    Root,
1756}