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