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, Orientation, Point, Rect, Role,
13    Toggled,
14};
15use accesskit_consumer::{FilterResult, Node, TreeState};
16use atspi_common::{
17    CoordType, Granularity, Interface, InterfaceSet, Layer, Live as AtspiLive, Role as AtspiRole,
18    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<'a> NodeWrapper<'a> {
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::PushButton
85                }
86            }
87            Role::DefaultButton => AtspiRole::PushButton,
88            Role::Canvas => AtspiRole::Canvas,
89            Role::Caption => AtspiRole::Caption,
90            Role::Cell => AtspiRole::TableCell,
91            Role::CheckBox => AtspiRole::CheckBox,
92            Role::Switch => AtspiRole::ToggleButton,
93            Role::ColorWell => AtspiRole::PushButton,
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 | Role::DescriptionListDetail => AtspiRole::DescriptionValue,
101            Role::DescriptionList => AtspiRole::DescriptionList,
102            Role::DescriptionListTerm => AtspiRole::DescriptionTerm,
103            Role::Details => AtspiRole::Panel,
104            Role::Dialog => AtspiRole::Dialog,
105            Role::Directory => AtspiRole::List,
106            Role::DisclosureTriangle => AtspiRole::ToggleButton,
107            Role::DocCover => AtspiRole::Image,
108            Role::DocBackLink | Role::DocBiblioRef | Role::DocGlossRef | Role::DocNoteRef => {
109                AtspiRole::Link
110            }
111            Role::DocBiblioEntry | Role::DocEndnote => AtspiRole::ListItem,
112            Role::DocNotice | Role::DocTip => AtspiRole::Comment,
113            Role::DocFootnote => AtspiRole::Footnote,
114            Role::DocPageBreak => AtspiRole::Separator,
115            Role::DocPageFooter => AtspiRole::Footer,
116            Role::DocPageHeader => AtspiRole::Header,
117            Role::DocAcknowledgements
118            | Role::DocAfterword
119            | Role::DocAppendix
120            | Role::DocBibliography
121            | Role::DocChapter
122            | Role::DocConclusion
123            | Role::DocCredits
124            | Role::DocEndnotes
125            | Role::DocEpilogue
126            | Role::DocErrata
127            | Role::DocForeword
128            | Role::DocGlossary
129            | Role::DocIndex
130            | Role::DocIntroduction
131            | Role::DocPageList
132            | Role::DocPart
133            | Role::DocPreface
134            | Role::DocPrologue
135            | Role::DocToc => AtspiRole::Landmark,
136            Role::DocAbstract
137            | Role::DocColophon
138            | Role::DocCredit
139            | Role::DocDedication
140            | Role::DocEpigraph
141            | Role::DocExample
142            | Role::DocPullquote
143            | Role::DocQna => AtspiRole::Section,
144            Role::DocSubtitle => AtspiRole::Heading,
145            Role::Document => AtspiRole::DocumentFrame,
146            Role::EmbeddedObject => AtspiRole::Embedded,
147            // TODO: Forms which lack an accessible name are no longer
148            // exposed as forms. Forms which have accessible
149            // names should be exposed as `AtspiRole::Landmark` according to Core AAM.
150            Role::Form => AtspiRole::Form,
151            Role::Figure | Role::Feed => AtspiRole::Panel,
152            Role::GenericContainer
153            | Role::FooterAsNonLandmark
154            | Role::HeaderAsNonLandmark
155            | Role::Ruby => AtspiRole::Section,
156            Role::GraphicsDocument => AtspiRole::DocumentFrame,
157            Role::GraphicsObject => AtspiRole::Panel,
158            Role::GraphicsSymbol => AtspiRole::Image,
159            Role::Grid => AtspiRole::Table,
160            Role::Group => AtspiRole::Panel,
161            Role::Heading => AtspiRole::Heading,
162            Role::Iframe | Role::IframePresentational => AtspiRole::InternalFrame,
163            // TODO: If there are unignored children, then it should be AtspiRole::ImageMap.
164            Role::Image => AtspiRole::Image,
165            Role::TextRun => AtspiRole::Static,
166            Role::Legend => AtspiRole::Label,
167            // Layout table objects are treated the same as `Role::GenericContainer`.
168            Role::LayoutTable => AtspiRole::Section,
169            Role::LayoutTableCell => AtspiRole::Section,
170            Role::LayoutTableRow => AtspiRole::Section,
171            // TODO: Having a separate accessible object for line breaks
172            // is inconsistent with other implementations.
173            Role::LineBreak => AtspiRole::Static,
174            Role::Link => AtspiRole::Link,
175            Role::List => AtspiRole::List,
176            Role::ListBox => AtspiRole::ListBox,
177            // TODO: Use `AtspiRole::MenuItem' inside a combo box.
178            Role::ListBoxOption => AtspiRole::ListItem,
179            Role::ListGrid => AtspiRole::Table,
180            Role::ListItem => AtspiRole::ListItem,
181            // Regular list markers only expose their alternative text, but do not
182            // expose their descendants, and the descendants should be ignored. This
183            // is because the alternative text depends on the counter style and can
184            // be different from the actual (visual) marker text, and hence,
185            // inconsistent with the descendants. We treat a list marker as non-text
186            // only if it still has non-ignored descendants, which happens only when =>
187            // - The list marker itself is ignored but the descendants are not
188            // - Or the list marker contains images
189            Role::ListMarker => AtspiRole::Static,
190            Role::Log => AtspiRole::Log,
191            Role::Main => AtspiRole::Landmark,
192            Role::Mark => AtspiRole::Static,
193            Role::Math => AtspiRole::Math,
194            Role::Marquee => AtspiRole::Marquee,
195            Role::Menu | Role::MenuListPopup => AtspiRole::Menu,
196            Role::MenuBar => AtspiRole::MenuBar,
197            Role::MenuItem | Role::MenuListOption => AtspiRole::MenuItem,
198            Role::MenuItemCheckBox => AtspiRole::CheckMenuItem,
199            Role::MenuItemRadio => AtspiRole::RadioMenuItem,
200            Role::Meter => AtspiRole::LevelBar,
201            Role::Navigation => AtspiRole::Landmark,
202            Role::Note => AtspiRole::Comment,
203            Role::Pane | Role::ScrollView => AtspiRole::Panel,
204            Role::Paragraph => AtspiRole::Paragraph,
205            Role::PdfActionableHighlight => AtspiRole::PushButton,
206            Role::PdfRoot => AtspiRole::DocumentFrame,
207            Role::PluginObject => AtspiRole::Embedded,
208            Role::Portal => AtspiRole::PushButton,
209            Role::Pre => AtspiRole::Section,
210            Role::ProgressIndicator => AtspiRole::ProgressBar,
211            Role::RadioButton => AtspiRole::RadioButton,
212            Role::RadioGroup => AtspiRole::Panel,
213            Role::Region => AtspiRole::Landmark,
214            Role::RootWebArea => AtspiRole::DocumentWeb,
215            Role::Row => AtspiRole::TableRow,
216            Role::RowGroup => AtspiRole::Panel,
217            Role::RowHeader => AtspiRole::RowHeader,
218            // TODO: Generally exposed as description on <ruby> (`Role::Ruby`) element, not
219            // as its own object in the tree.
220            // However, it's possible to make a `Role::RubyAnnotation` element show up in the
221            // tree, for example by adding tabindex="0" to the source <rp> or <rt>
222            // element or making the source element the target of an aria-owns.
223            // Therefore, we need to gracefully handle it if it actually
224            // shows up in the tree.
225            Role::RubyAnnotation => AtspiRole::Static,
226            Role::Section => AtspiRole::Section,
227            Role::ScrollBar => AtspiRole::ScrollBar,
228            Role::Search => AtspiRole::Landmark,
229            Role::Slider => AtspiRole::Slider,
230            Role::SpinButton => AtspiRole::SpinButton,
231            Role::Splitter => AtspiRole::Separator,
232            Role::Label => AtspiRole::Label,
233            Role::Status => AtspiRole::StatusBar,
234            Role::SvgRoot => AtspiRole::DocumentFrame,
235            Role::Tab => AtspiRole::PageTab,
236            Role::Table => AtspiRole::Table,
237            Role::TabList => AtspiRole::PageTabList,
238            Role::TabPanel => AtspiRole::ScrollPane,
239            // TODO: This mapping should also be applied to the dfn
240            // element.
241            Role::Term => AtspiRole::DescriptionTerm,
242            Role::TitleBar => AtspiRole::TitleBar,
243            Role::TextInput
244            | Role::MultilineTextInput
245            | Role::SearchInput
246            | Role::EmailInput
247            | Role::NumberInput
248            | Role::PhoneNumberInput
249            | Role::UrlInput => AtspiRole::Entry,
250            Role::DateInput
251            | Role::DateTimeInput
252            | Role::WeekInput
253            | Role::MonthInput
254            | Role::TimeInput => AtspiRole::DateEditor,
255            Role::PasswordInput => AtspiRole::PasswordText,
256            Role::Abbr | Role::Code | Role::Emphasis | Role::Strong | Role::Time => {
257                AtspiRole::Static
258            }
259            Role::Timer => AtspiRole::Timer,
260            Role::Toolbar => AtspiRole::ToolBar,
261            Role::Tooltip => AtspiRole::ToolTip,
262            Role::Tree => AtspiRole::Tree,
263            Role::TreeItem => AtspiRole::TreeItem,
264            Role::TreeGrid => AtspiRole::TreeTable,
265            Role::Video => AtspiRole::Video,
266            // In AT-SPI, elements with `AtspiRole::Frame` are windows with titles and
267            // buttons, while those with `AtspiRole::Window` are windows without those
268            // elements.
269            Role::Window => AtspiRole::Frame,
270            Role::WebView => AtspiRole::Panel,
271            Role::FigureCaption => AtspiRole::Caption,
272            // TODO: Are there special cases to consider?
273            Role::Unknown => AtspiRole::Unknown,
274            Role::ImeCandidate | Role::Keyboard => AtspiRole::RedundantObject,
275            Role::Terminal => AtspiRole::Terminal,
276        }
277    }
278
279    fn is_focused(&self) -> bool {
280        self.0.is_focused()
281    }
282
283    pub(crate) fn state(&self, is_window_focused: bool) -> StateSet {
284        let state = self.0;
285        let atspi_role = self.role();
286        let mut atspi_state = StateSet::empty();
287        if state.parent_id().is_none() && state.role() == Role::Window && is_window_focused {
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() {
295            atspi_state.insert(State::Focusable);
296        }
297        if let Some(orientation) = state.orientation() {
298            atspi_state.insert(if orientation == Orientation::Horizontal {
299                State::Horizontal
300            } else {
301                State::Vertical
302            });
303        }
304        let filter_result = filter(self.0);
305        if filter_result == FilterResult::Include {
306            atspi_state.insert(State::Visible | State::Showing);
307        }
308        if atspi_role != AtspiRole::ToggleButton && state.toggled().is_some() {
309            atspi_state.insert(State::Checkable);
310        }
311        if let Some(selected) = state.is_selected() {
312            if !state.is_disabled() {
313                atspi_state.insert(State::Selectable);
314            }
315            if selected {
316                atspi_state.insert(State::Selected);
317            }
318        }
319        if state.is_text_input() {
320            atspi_state.insert(State::SelectableText);
321            atspi_state.insert(match state.is_multiline() {
322                true => State::MultiLine,
323                false => State::SingleLine,
324            });
325        }
326
327        // Special case for indeterminate progressbar.
328        if state.role() == Role::ProgressIndicator && state.numeric_value().is_none() {
329            atspi_state.insert(State::Indeterminate);
330        }
331
332        // Toggled state
333        match state.toggled() {
334            Some(Toggled::Mixed) => atspi_state.insert(State::Indeterminate),
335            Some(Toggled::True) if atspi_role == AtspiRole::ToggleButton => {
336                atspi_state.insert(State::Pressed)
337            }
338            Some(Toggled::True) => atspi_state.insert(State::Checked),
339            _ => {}
340        }
341
342        if state.is_read_only_supported() && state.is_read_only_or_disabled() {
343            atspi_state.insert(State::ReadOnly);
344        } else {
345            atspi_state.insert(State::Enabled | State::Sensitive);
346        }
347
348        if self.is_focused() {
349            atspi_state.insert(State::Focused);
350        }
351
352        atspi_state
353    }
354
355    fn attributes(&self) -> HashMap<&'static str, String> {
356        let mut attributes = HashMap::new();
357        if let Some(placeholder) = self.0.placeholder() {
358            attributes.insert("placeholder-text", placeholder);
359        }
360        attributes
361    }
362
363    fn is_root(&self) -> bool {
364        self.0.is_root()
365    }
366
367    fn supports_action(&self) -> bool {
368        self.0.is_clickable()
369    }
370
371    fn supports_component(&self) -> bool {
372        self.0.raw_bounds().is_some() || self.is_root()
373    }
374
375    fn supports_text(&self) -> bool {
376        self.0.supports_text_ranges()
377    }
378
379    fn supports_value(&self) -> bool {
380        self.current_value().is_some()
381    }
382
383    pub(crate) fn interfaces(&self) -> InterfaceSet {
384        let mut interfaces = InterfaceSet::new(Interface::Accessible);
385        if self.supports_action() {
386            interfaces.insert(Interface::Action);
387        }
388        if self.supports_component() {
389            interfaces.insert(Interface::Component);
390        }
391        if self.supports_text() {
392            interfaces.insert(Interface::Text);
393        }
394        if self.supports_value() {
395            interfaces.insert(Interface::Value);
396        }
397        interfaces
398    }
399
400    pub(crate) fn live(&self) -> AtspiLive {
401        let live = self.0.live();
402        match live {
403            Live::Off => AtspiLive::None,
404            Live::Polite => AtspiLive::Polite,
405            Live::Assertive => AtspiLive::Assertive,
406        }
407    }
408
409    fn n_actions(&self) -> i32 {
410        if self.0.is_clickable() {
411            1
412        } else {
413            0
414        }
415    }
416
417    fn get_action_name(&self, index: i32) -> String {
418        if index != 0 {
419            return String::new();
420        }
421        String::from(if self.0.is_clickable() { "click" } else { "" })
422    }
423
424    fn raw_bounds_and_transform(&self) -> (Option<Rect>, Affine) {
425        let state = self.0;
426        (state.raw_bounds(), state.direct_transform())
427    }
428
429    fn extents(&self, window_bounds: &WindowBounds, coord_type: CoordType) -> Option<Rect> {
430        let mut bounds = self.0.bounding_box();
431        if self.is_root() {
432            let window_bounds = window_bounds.inner.with_origin(Point::ZERO);
433            if !window_bounds.is_empty() {
434                if let Some(bounds) = &mut bounds {
435                    bounds.x0 = bounds.x0.min(window_bounds.x1);
436                    bounds.y0 = bounds.y0.min(window_bounds.y1);
437                    bounds.x1 = bounds.x1.min(window_bounds.x1);
438                    bounds.y1 = bounds.y1.min(window_bounds.y1);
439                } else {
440                    bounds = Some(window_bounds);
441                }
442            }
443        }
444        bounds.map(|bounds| {
445            let new_origin = window_bounds.accesskit_point_to_atspi_point(
446                bounds.origin(),
447                self.0.filtered_parent(&filter),
448                coord_type,
449            );
450            bounds.with_origin(new_origin)
451        })
452    }
453
454    fn current_value(&self) -> Option<f64> {
455        self.0.numeric_value()
456    }
457
458    pub(crate) fn notify_changes(
459        &self,
460        window_bounds: &WindowBounds,
461        adapter: &Adapter,
462        old: &NodeWrapper<'_>,
463    ) {
464        self.notify_state_changes(adapter, old);
465        self.notify_property_changes(adapter, old);
466        self.notify_bounds_changes(window_bounds, adapter, old);
467        self.notify_children_changes(adapter, old);
468    }
469
470    fn notify_state_changes(&self, adapter: &Adapter, old: &NodeWrapper<'_>) {
471        let old_state = old.state(true);
472        let new_state = self.state(true);
473        let changed_states = old_state ^ new_state;
474        for state in changed_states.iter() {
475            if state == State::Focused {
476                // This is handled specially in `focus_moved`.
477                continue;
478            }
479            adapter.emit_object_event(
480                self.id(),
481                ObjectEvent::StateChanged(state, new_state.contains(state)),
482            );
483        }
484    }
485
486    fn notify_property_changes(&self, adapter: &Adapter, old: &NodeWrapper<'_>) {
487        let name = self.name();
488        if name != old.name() {
489            let name = name.unwrap_or_default();
490            adapter.emit_object_event(
491                self.id(),
492                ObjectEvent::PropertyChanged(Property::Name(name.clone())),
493            );
494
495            let live = self.live();
496            if live != AtspiLive::None {
497                adapter.emit_object_event(self.id(), ObjectEvent::Announcement(name, live));
498            }
499        }
500        let description = self.description();
501        if description != old.description() {
502            adapter.emit_object_event(
503                self.id(),
504                ObjectEvent::PropertyChanged(Property::Description(
505                    description.unwrap_or_default(),
506                )),
507            );
508        }
509        let parent_id = self.parent_id();
510        if parent_id != old.parent_id() {
511            let parent = self
512                .0
513                .filtered_parent(&filter)
514                .map_or(NodeIdOrRoot::Root, |node| NodeIdOrRoot::Node(node.id()));
515            adapter.emit_object_event(
516                self.id(),
517                ObjectEvent::PropertyChanged(Property::Parent(parent)),
518            );
519        }
520        let role = self.role();
521        if role != old.role() {
522            adapter.emit_object_event(
523                self.id(),
524                ObjectEvent::PropertyChanged(Property::Role(role)),
525            );
526        }
527        if let Some(value) = self.current_value() {
528            if Some(value) != old.current_value() {
529                adapter.emit_object_event(
530                    self.id(),
531                    ObjectEvent::PropertyChanged(Property::Value(value)),
532                );
533            }
534        }
535    }
536
537    fn notify_bounds_changes(
538        &self,
539        window_bounds: &WindowBounds,
540        adapter: &Adapter,
541        old: &NodeWrapper<'_>,
542    ) {
543        if self.raw_bounds_and_transform() != old.raw_bounds_and_transform() {
544            if let Some(extents) = self.extents(window_bounds, CoordType::Window) {
545                adapter.emit_object_event(self.id(), ObjectEvent::BoundsChanged(extents.into()));
546            }
547        }
548    }
549
550    fn notify_children_changes(&self, adapter: &Adapter, old: &NodeWrapper<'_>) {
551        let old_filtered_children = old.filtered_child_ids().collect::<Vec<NodeId>>();
552        let new_filtered_children = self.filtered_child_ids().collect::<Vec<NodeId>>();
553        for (index, child) in new_filtered_children.iter().enumerate() {
554            if !old_filtered_children.contains(child) {
555                adapter.emit_object_event(self.id(), ObjectEvent::ChildAdded(index, *child));
556            }
557        }
558        for child in old_filtered_children.into_iter() {
559            if !new_filtered_children.contains(&child) {
560                adapter.emit_object_event(self.id(), ObjectEvent::ChildRemoved(child));
561            }
562        }
563    }
564}
565
566#[derive(Clone)]
567pub struct PlatformNode {
568    context: Weak<Context>,
569    adapter_id: usize,
570    id: NodeId,
571}
572
573impl PlatformNode {
574    pub(crate) fn new(context: &Arc<Context>, adapter_id: usize, id: NodeId) -> Self {
575        Self {
576            context: Arc::downgrade(context),
577            adapter_id,
578            id,
579        }
580    }
581
582    fn from_adapter_root(adapter_id_and_context: &(usize, Arc<Context>)) -> Self {
583        let (adapter_id, context) = adapter_id_and_context;
584        Self::new(context, *adapter_id, context.read_tree().state().root_id())
585    }
586
587    fn upgrade_context(&self) -> Result<Arc<Context>> {
588        if let Some(context) = self.context.upgrade() {
589            Ok(context)
590        } else {
591            Err(Error::Defunct)
592        }
593    }
594
595    fn with_tree_state<F, T>(&self, f: F) -> Result<T>
596    where
597        F: FnOnce(&TreeState) -> Result<T>,
598    {
599        let context = self.upgrade_context()?;
600        let tree = context.read_tree();
601        f(tree.state())
602    }
603
604    fn with_tree_state_and_context<F, T>(&self, f: F) -> Result<T>
605    where
606        F: FnOnce(&TreeState, &Context) -> Result<T>,
607    {
608        let context = self.upgrade_context()?;
609        let tree = context.read_tree();
610        f(tree.state(), &context)
611    }
612
613    fn resolve_with_context<F, T>(&self, f: F) -> Result<T>
614    where
615        for<'a> F: FnOnce(Node<'a>, &Context) -> Result<T>,
616    {
617        self.with_tree_state_and_context(|state, context| {
618            if let Some(node) = state.node_by_id(self.id) {
619                f(node, context)
620            } else {
621                Err(Error::Defunct)
622            }
623        })
624    }
625
626    fn resolve_for_text_with_context<F, T>(&self, f: F) -> Result<T>
627    where
628        for<'a> F: FnOnce(Node<'a>, &Context) -> Result<T>,
629    {
630        self.resolve_with_context(|node, context| {
631            let wrapper = NodeWrapper(&node);
632            if wrapper.supports_text() {
633                f(node, context)
634            } else {
635                Err(Error::UnsupportedInterface)
636            }
637        })
638    }
639
640    fn resolve<F, T>(&self, f: F) -> Result<T>
641    where
642        for<'a> F: FnOnce(Node<'a>) -> Result<T>,
643    {
644        self.resolve_with_context(|node, _| f(node))
645    }
646
647    fn resolve_for_text<F, T>(&self, f: F) -> Result<T>
648    where
649        for<'a> F: FnOnce(Node<'a>) -> Result<T>,
650    {
651        self.resolve_for_text_with_context(|node, _| f(node))
652    }
653
654    fn do_action_internal<F>(&self, f: F) -> Result<()>
655    where
656        F: FnOnce(&TreeState, &Context) -> ActionRequest,
657    {
658        let context = self.upgrade_context()?;
659        let tree = context.read_tree();
660        if tree.state().has_node(self.id) {
661            let request = f(tree.state(), &context);
662            drop(tree);
663            context.do_action(request);
664            Ok(())
665        } else {
666            Err(Error::Defunct)
667        }
668    }
669
670    pub fn name(&self) -> Result<String> {
671        self.resolve(|node| {
672            let wrapper = NodeWrapper(&node);
673            Ok(wrapper.name().unwrap_or_default())
674        })
675    }
676
677    pub fn description(&self) -> Result<String> {
678        self.resolve(|node| {
679            let wrapper = NodeWrapper(&node);
680            Ok(wrapper.description().unwrap_or_default())
681        })
682    }
683
684    pub fn relative(&self, id: NodeId) -> Self {
685        Self {
686            context: self.context.clone(),
687            adapter_id: self.adapter_id,
688            id,
689        }
690    }
691
692    pub fn root(&self) -> Result<PlatformRoot> {
693        let context = self.upgrade_context()?;
694        Ok(PlatformRoot::new(&context.app_context))
695    }
696
697    pub fn toolkit_name(&self) -> Result<String> {
698        self.with_tree_state(|state| Ok(state.toolkit_name().unwrap_or_default()))
699    }
700
701    pub fn toolkit_version(&self) -> Result<String> {
702        self.with_tree_state(|state| Ok(state.toolkit_version().unwrap_or_default()))
703    }
704
705    pub fn parent(&self) -> Result<NodeIdOrRoot> {
706        self.resolve(|node| {
707            let parent = node
708                .filtered_parent(&filter)
709                .map_or(NodeIdOrRoot::Root, |node| NodeIdOrRoot::Node(node.id()));
710            Ok(parent)
711        })
712    }
713
714    pub fn child_count(&self) -> Result<i32> {
715        self.resolve(|node| {
716            i32::try_from(node.filtered_children(&filter).count())
717                .map_err(|_| Error::TooManyChildren)
718        })
719    }
720
721    pub fn adapter_id(&self) -> usize {
722        self.adapter_id
723    }
724
725    pub fn id(&self) -> NodeId {
726        self.id
727    }
728
729    pub fn accessible_id(&self) -> Result<String> {
730        self.resolve(|node| {
731            if let Some(author_id) = node.author_id() {
732                Ok(author_id.to_string())
733            } else {
734                Ok(String::new())
735            }
736        })
737    }
738
739    pub fn child_at_index(&self, index: usize) -> Result<Option<NodeId>> {
740        self.resolve(|node| {
741            let child = node
742                .filtered_children(&filter)
743                .nth(index)
744                .map(|child| child.id());
745            Ok(child)
746        })
747    }
748
749    pub fn map_children<T, I>(&self, f: impl Fn(NodeId) -> I) -> Result<T>
750    where
751        T: FromIterator<I>,
752    {
753        self.resolve(|node| {
754            let children = node
755                .filtered_children(&filter)
756                .map(|child| child.id())
757                .map(f)
758                .collect();
759            Ok(children)
760        })
761    }
762
763    pub fn index_in_parent(&self) -> Result<i32> {
764        self.resolve(|node| {
765            i32::try_from(node.preceding_filtered_siblings(&filter).count())
766                .map_err(|_| Error::IndexOutOfRange)
767        })
768    }
769
770    pub fn role(&self) -> Result<AtspiRole> {
771        self.resolve(|node| {
772            let wrapper = NodeWrapper(&node);
773            Ok(wrapper.role())
774        })
775    }
776
777    pub fn localized_role_name(&self) -> Result<String> {
778        self.resolve(|node| Ok(node.role_description().unwrap_or_default()))
779    }
780
781    pub fn state(&self) -> StateSet {
782        self.resolve_with_context(|node, context| {
783            let wrapper = NodeWrapper(&node);
784            Ok(wrapper.state(context.read_tree().state().focus_id().is_some()))
785        })
786        .unwrap_or(State::Defunct.into())
787    }
788
789    pub fn attributes(&self) -> Result<HashMap<&'static str, String>> {
790        self.resolve(|node| {
791            let wrapper = NodeWrapper(&node);
792            Ok(wrapper.attributes())
793        })
794    }
795
796    pub fn supports_action(&self) -> Result<bool> {
797        self.resolve(|node| {
798            let wrapper = NodeWrapper(&node);
799            Ok(wrapper.supports_action())
800        })
801    }
802
803    pub fn supports_component(&self) -> Result<bool> {
804        self.resolve(|node| {
805            let wrapper = NodeWrapper(&node);
806            Ok(wrapper.supports_component())
807        })
808    }
809
810    pub fn supports_text(&self) -> Result<bool> {
811        self.resolve(|node| {
812            let wrapper = NodeWrapper(&node);
813            Ok(wrapper.supports_text())
814        })
815    }
816
817    pub fn supports_value(&self) -> Result<bool> {
818        self.resolve(|node| {
819            let wrapper = NodeWrapper(&node);
820            Ok(wrapper.supports_value())
821        })
822    }
823
824    pub fn interfaces(&self) -> Result<InterfaceSet> {
825        self.resolve(|node| {
826            let wrapper = NodeWrapper(&node);
827            Ok(wrapper.interfaces())
828        })
829    }
830
831    pub fn n_actions(&self) -> Result<i32> {
832        self.resolve(|node| {
833            let wrapper = NodeWrapper(&node);
834            Ok(wrapper.n_actions())
835        })
836    }
837
838    pub fn action_name(&self, index: i32) -> Result<String> {
839        self.resolve(|node| {
840            let wrapper = NodeWrapper(&node);
841            Ok(wrapper.get_action_name(index))
842        })
843    }
844
845    pub fn actions(&self) -> Result<Vec<AtspiAction>> {
846        self.resolve(|node| {
847            let wrapper = NodeWrapper(&node);
848            let n_actions = wrapper.n_actions() as usize;
849            let mut actions = Vec::with_capacity(n_actions);
850            for i in 0..n_actions {
851                actions.push(AtspiAction {
852                    localized_name: wrapper.get_action_name(i as i32),
853                    description: "".into(),
854                    key_binding: "".into(),
855                });
856            }
857            Ok(actions)
858        })
859    }
860
861    pub fn do_action(&self, index: i32) -> Result<bool> {
862        if index != 0 {
863            return Ok(false);
864        }
865        self.do_action_internal(|_, _| ActionRequest {
866            action: Action::Click,
867            target: self.id,
868            data: None,
869        })?;
870        Ok(true)
871    }
872
873    pub fn contains(&self, x: i32, y: i32, coord_type: CoordType) -> Result<bool> {
874        self.resolve_with_context(|node, context| {
875            let window_bounds = context.read_root_window_bounds();
876            let wrapper = NodeWrapper(&node);
877            if let Some(extents) = wrapper.extents(&window_bounds, coord_type) {
878                Ok(extents.contains(Point::new(x.into(), y.into())))
879            } else {
880                Ok(false)
881            }
882        })
883    }
884
885    pub fn accessible_at_point(
886        &self,
887        x: i32,
888        y: i32,
889        coord_type: CoordType,
890    ) -> Result<Option<NodeId>> {
891        self.resolve_with_context(|node, context| {
892            let window_bounds = context.read_root_window_bounds();
893            let point = window_bounds.atspi_point_to_accesskit_point(
894                Point::new(x.into(), y.into()),
895                Some(node),
896                coord_type,
897            );
898            let point = node.transform().inverse() * point;
899            Ok(node.node_at_point(point, &filter).map(|node| node.id()))
900        })
901    }
902
903    pub fn extents(&self, coord_type: CoordType) -> Result<AtspiRect> {
904        self.resolve_with_context(|node, context| {
905            let window_bounds = context.read_root_window_bounds();
906            let wrapper = NodeWrapper(&node);
907            Ok(wrapper
908                .extents(&window_bounds, coord_type)
909                .map_or(AtspiRect::INVALID, AtspiRect::from))
910        })
911    }
912
913    pub fn layer(&self) -> Result<Layer> {
914        self.resolve(|node| {
915            let wrapper = NodeWrapper(&node);
916            if wrapper.role() == AtspiRole::Window {
917                Ok(Layer::Window)
918            } else {
919                Ok(Layer::Widget)
920            }
921        })
922    }
923
924    pub fn grab_focus(&self) -> Result<bool> {
925        self.do_action_internal(|_, _| ActionRequest {
926            action: Action::Focus,
927            target: self.id,
928            data: None,
929        })?;
930        Ok(true)
931    }
932
933    pub fn scroll_to_point(&self, coord_type: CoordType, x: i32, y: i32) -> Result<bool> {
934        self.resolve_with_context(|node, context| {
935            let window_bounds = context.read_root_window_bounds();
936            let point = window_bounds.atspi_point_to_accesskit_point(
937                Point::new(x.into(), y.into()),
938                node.filtered_parent(&filter),
939                coord_type,
940            );
941            context.do_action(ActionRequest {
942                action: Action::ScrollToPoint,
943                target: self.id,
944                data: Some(ActionData::ScrollToPoint(point)),
945            });
946            Ok(())
947        })?;
948        Ok(true)
949    }
950
951    pub fn character_count(&self) -> Result<i32> {
952        self.resolve_for_text(|node| {
953            node.document_range()
954                .end()
955                .to_global_usv_index()
956                .try_into()
957                .map_err(|_| Error::TooManyCharacters)
958        })
959    }
960
961    pub fn caret_offset(&self) -> Result<i32> {
962        self.resolve_for_text(|node| {
963            node.text_selection_focus().map_or(Ok(-1), |focus| {
964                focus
965                    .to_global_usv_index()
966                    .try_into()
967                    .map_err(|_| Error::TooManyCharacters)
968            })
969        })
970    }
971
972    pub fn string_at_offset(
973        &self,
974        offset: i32,
975        granularity: Granularity,
976    ) -> Result<(String, i32, i32)> {
977        self.resolve_for_text(|node| {
978            let range = text_range_from_offset(&node, offset, granularity)?;
979            let text = range.text();
980            let start = range
981                .start()
982                .to_global_usv_index()
983                .try_into()
984                .map_err(|_| Error::TooManyCharacters)?;
985            let end = range
986                .end()
987                .to_global_usv_index()
988                .try_into()
989                .map_err(|_| Error::TooManyCharacters)?;
990
991            Ok((text, start, end))
992        })
993    }
994
995    pub fn text(&self, start_offset: i32, end_offset: i32) -> Result<String> {
996        self.resolve_for_text(|node| {
997            let range = text_range_from_offsets(&node, start_offset, end_offset)
998                .ok_or(Error::IndexOutOfRange)?;
999            Ok(range.text())
1000        })
1001    }
1002
1003    pub fn set_caret_offset(&self, offset: i32) -> Result<bool> {
1004        self.resolve_for_text_with_context(|node, context| {
1005            let offset = text_position_from_offset(&node, offset).ok_or(Error::IndexOutOfRange)?;
1006            context.do_action(ActionRequest {
1007                action: Action::SetTextSelection,
1008                target: node.id(),
1009                data: Some(ActionData::SetTextSelection(
1010                    offset.to_degenerate_range().to_text_selection(),
1011                )),
1012            });
1013            Ok(true)
1014        })
1015    }
1016
1017    pub fn text_attribute_value(&self, _offset: i32, _attribute_name: &str) -> Result<String> {
1018        // TODO: Implement rich text.
1019        Err(Error::UnsupportedInterface)
1020    }
1021
1022    pub fn text_attributes(&self, _offset: i32) -> Result<(HashMap<String, String>, i32, i32)> {
1023        // TODO: Implement rich text.
1024        Err(Error::UnsupportedInterface)
1025    }
1026
1027    pub fn default_text_attributes(&self) -> Result<HashMap<String, String>> {
1028        // TODO: Implement rich text.
1029        Err(Error::UnsupportedInterface)
1030    }
1031
1032    pub fn character_extents(&self, offset: i32, coord_type: CoordType) -> Result<AtspiRect> {
1033        self.resolve_for_text_with_context(|node, context| {
1034            let range = text_range_from_offset(&node, offset, Granularity::Char)?;
1035            if let Some(bounds) = range.bounding_boxes().first() {
1036                let window_bounds = context.read_root_window_bounds();
1037                let new_origin = window_bounds.accesskit_point_to_atspi_point(
1038                    bounds.origin(),
1039                    Some(node),
1040                    coord_type,
1041                );
1042                Ok(bounds.with_origin(new_origin).into())
1043            } else {
1044                Ok(AtspiRect::INVALID)
1045            }
1046        })
1047    }
1048
1049    pub fn offset_at_point(&self, x: i32, y: i32, coord_type: CoordType) -> Result<i32> {
1050        self.resolve_for_text_with_context(|node, context| {
1051            let window_bounds = context.read_root_window_bounds();
1052            let point = window_bounds.atspi_point_to_accesskit_point(
1053                Point::new(x.into(), y.into()),
1054                Some(node),
1055                coord_type,
1056            );
1057            let point = node.transform().inverse() * point;
1058            node.text_position_at_point(point)
1059                .to_global_usv_index()
1060                .try_into()
1061                .map_err(|_| Error::TooManyCharacters)
1062        })
1063    }
1064
1065    pub fn n_selections(&self) -> Result<i32> {
1066        self.resolve_for_text(|node| {
1067            match node.text_selection().filter(|range| !range.is_degenerate()) {
1068                Some(_) => Ok(1),
1069                None => Ok(0),
1070            }
1071        })
1072    }
1073
1074    pub fn selection(&self, selection_num: i32) -> Result<(i32, i32)> {
1075        if selection_num != 0 {
1076            return Ok((-1, -1));
1077        }
1078
1079        self.resolve_for_text(|node| {
1080            node.text_selection()
1081                .filter(|range| !range.is_degenerate())
1082                .map_or(Ok((-1, -1)), |range| {
1083                    let start = range
1084                        .start()
1085                        .to_global_usv_index()
1086                        .try_into()
1087                        .map_err(|_| Error::TooManyCharacters)?;
1088                    let end = range
1089                        .end()
1090                        .to_global_usv_index()
1091                        .try_into()
1092                        .map_err(|_| Error::TooManyCharacters)?;
1093
1094                    Ok((start, end))
1095                })
1096        })
1097    }
1098
1099    pub fn add_selection(&self, start_offset: i32, end_offset: i32) -> Result<bool> {
1100        // We only support one selection.
1101        self.set_selection(0, start_offset, end_offset)
1102    }
1103
1104    pub fn remove_selection(&self, selection_num: i32) -> Result<bool> {
1105        if selection_num != 0 {
1106            return Ok(false);
1107        }
1108
1109        self.resolve_for_text_with_context(|node, context| {
1110            // Simply collapse the selection to the position of the caret if a caret is
1111            // visible, otherwise set the selection to 0.
1112            let selection_end = node
1113                .text_selection_focus()
1114                .unwrap_or_else(|| node.document_range().start());
1115            context.do_action(ActionRequest {
1116                action: Action::SetTextSelection,
1117                target: node.id(),
1118                data: Some(ActionData::SetTextSelection(
1119                    selection_end.to_degenerate_range().to_text_selection(),
1120                )),
1121            });
1122            Ok(true)
1123        })
1124    }
1125
1126    pub fn set_selection(
1127        &self,
1128        selection_num: i32,
1129        start_offset: i32,
1130        end_offset: i32,
1131    ) -> Result<bool> {
1132        if selection_num != 0 {
1133            return Ok(false);
1134        }
1135
1136        self.resolve_for_text_with_context(|node, context| {
1137            let range = text_range_from_offsets(&node, start_offset, end_offset)
1138                .ok_or(Error::IndexOutOfRange)?;
1139            context.do_action(ActionRequest {
1140                action: Action::SetTextSelection,
1141                target: node.id(),
1142                data: Some(ActionData::SetTextSelection(range.to_text_selection())),
1143            });
1144            Ok(true)
1145        })
1146    }
1147
1148    pub fn range_extents(
1149        &self,
1150        start_offset: i32,
1151        end_offset: i32,
1152        coord_type: CoordType,
1153    ) -> Result<AtspiRect> {
1154        self.resolve_for_text_with_context(|node, context| {
1155            if let Some(rect) = text_range_bounds_from_offsets(&node, start_offset, end_offset) {
1156                let window_bounds = context.read_root_window_bounds();
1157                let new_origin = window_bounds.accesskit_point_to_atspi_point(
1158                    rect.origin(),
1159                    Some(node),
1160                    coord_type,
1161                );
1162                Ok(rect.with_origin(new_origin).into())
1163            } else {
1164                Ok(AtspiRect::INVALID)
1165            }
1166        })
1167    }
1168
1169    pub fn text_attribute_run(
1170        &self,
1171        _offset: i32,
1172        _include_defaults: bool,
1173    ) -> Result<(HashMap<String, String>, i32, i32)> {
1174        // TODO: Implement rich text.
1175        // For now, just report a range spanning the entire text with no attributes,
1176        // this is required by Orca to announce selection content and caret movements.
1177        let character_count = self.character_count()?;
1178        Ok((HashMap::new(), 0, character_count))
1179    }
1180
1181    pub fn scroll_substring_to(
1182        &self,
1183        start_offset: i32,
1184        end_offset: i32,
1185        _: ScrollType,
1186    ) -> Result<bool> {
1187        self.resolve_for_text_with_context(|node, context| {
1188            if let Some(rect) = text_range_bounds_from_offsets(&node, start_offset, end_offset) {
1189                context.do_action(ActionRequest {
1190                    action: Action::ScrollIntoView,
1191                    target: node.id(),
1192                    data: Some(ActionData::ScrollTargetRect(rect)),
1193                });
1194                Ok(true)
1195            } else {
1196                Ok(false)
1197            }
1198        })
1199    }
1200
1201    pub fn scroll_substring_to_point(
1202        &self,
1203        start_offset: i32,
1204        end_offset: i32,
1205        coord_type: CoordType,
1206        x: i32,
1207        y: i32,
1208    ) -> Result<bool> {
1209        self.resolve_for_text_with_context(|node, context| {
1210            let window_bounds = context.read_root_window_bounds();
1211            let target_point = window_bounds.atspi_point_to_accesskit_point(
1212                Point::new(x.into(), y.into()),
1213                Some(node),
1214                coord_type,
1215            );
1216
1217            if let Some(rect) = text_range_bounds_from_offsets(&node, start_offset, end_offset) {
1218                let point = Point::new(target_point.x - rect.x0, target_point.y - rect.y0);
1219                context.do_action(ActionRequest {
1220                    action: Action::ScrollToPoint,
1221                    target: node.id(),
1222                    data: Some(ActionData::ScrollToPoint(point)),
1223                });
1224                return Ok(true);
1225            }
1226            Ok(false)
1227        })
1228    }
1229
1230    pub fn minimum_value(&self) -> Result<f64> {
1231        self.resolve(|node| Ok(node.min_numeric_value().unwrap_or(f64::MIN)))
1232    }
1233
1234    pub fn maximum_value(&self) -> Result<f64> {
1235        self.resolve(|node| Ok(node.max_numeric_value().unwrap_or(f64::MAX)))
1236    }
1237
1238    pub fn minimum_increment(&self) -> Result<f64> {
1239        self.resolve(|node| Ok(node.numeric_value_step().unwrap_or(0.0)))
1240    }
1241
1242    pub fn current_value(&self) -> Result<f64> {
1243        self.resolve(|node| {
1244            let wrapper = NodeWrapper(&node);
1245            Ok(wrapper.current_value().unwrap_or(0.0))
1246        })
1247    }
1248
1249    pub fn set_current_value(&self, value: f64) -> Result<()> {
1250        self.do_action_internal(|_, _| ActionRequest {
1251            action: Action::SetValue,
1252            target: self.id,
1253            data: Some(ActionData::NumericValue(value)),
1254        })
1255    }
1256}
1257
1258impl PartialEq for PlatformNode {
1259    fn eq(&self, other: &Self) -> bool {
1260        self.adapter_id == other.adapter_id && self.id == other.id
1261    }
1262}
1263
1264impl Eq for PlatformNode {}
1265
1266impl Hash for PlatformNode {
1267    fn hash<H: Hasher>(&self, state: &mut H) {
1268        self.adapter_id.hash(state);
1269        self.id.hash(state);
1270    }
1271}
1272
1273#[derive(Clone)]
1274pub struct PlatformRoot {
1275    app_context: Weak<RwLock<AppContext>>,
1276}
1277
1278impl PlatformRoot {
1279    pub fn new(app_context: &Arc<RwLock<AppContext>>) -> Self {
1280        Self {
1281            app_context: Arc::downgrade(app_context),
1282        }
1283    }
1284
1285    fn resolve_app_context<F, T>(&self, f: F) -> Result<T>
1286    where
1287        for<'a> F: FnOnce(RwLockReadGuard<'a, AppContext>) -> Result<T>,
1288    {
1289        let app_context = match self.app_context.upgrade() {
1290            Some(context) => context,
1291            None => return Err(Error::Defunct),
1292        };
1293        let app_context = app_context.read().unwrap();
1294        f(app_context)
1295    }
1296
1297    pub fn name(&self) -> Result<String> {
1298        self.resolve_app_context(|context| Ok(context.name.clone().unwrap_or_default()))
1299    }
1300
1301    pub fn child_count(&self) -> Result<i32> {
1302        self.resolve_app_context(|context| {
1303            i32::try_from(context.adapters.len()).map_err(|_| Error::TooManyChildren)
1304        })
1305    }
1306
1307    pub fn child_at_index(&self, index: usize) -> Result<Option<PlatformNode>> {
1308        self.resolve_app_context(|context| {
1309            let child = context
1310                .adapters
1311                .get(index)
1312                .map(PlatformNode::from_adapter_root);
1313            Ok(child)
1314        })
1315    }
1316
1317    pub fn child_id_at_index(&self, index: usize) -> Result<Option<(usize, NodeId)>> {
1318        self.resolve_app_context(|context| {
1319            let child = context
1320                .adapters
1321                .get(index)
1322                .map(|(adapter_id, context)| (*adapter_id, context.read_tree().state().root_id()));
1323            Ok(child)
1324        })
1325    }
1326
1327    pub fn map_children<T, I>(&self, f: impl Fn(PlatformNode) -> I) -> Result<T>
1328    where
1329        T: FromIterator<I>,
1330    {
1331        self.resolve_app_context(|context| {
1332            let children = context
1333                .adapters
1334                .iter()
1335                .map(PlatformNode::from_adapter_root)
1336                .map(f)
1337                .collect();
1338            Ok(children)
1339        })
1340    }
1341
1342    pub fn map_child_ids<T, I>(&self, f: impl Fn((usize, NodeId)) -> I) -> Result<T>
1343    where
1344        T: FromIterator<I>,
1345    {
1346        self.resolve_app_context(|context| {
1347            let children = context
1348                .adapters
1349                .iter()
1350                .map(|(adapter_id, context)| (*adapter_id, context.read_tree().state().root_id()))
1351                .map(f)
1352                .collect();
1353            Ok(children)
1354        })
1355    }
1356
1357    pub fn toolkit_name(&self) -> Result<String> {
1358        self.resolve_app_context(|context| Ok(context.toolkit_name.clone().unwrap_or_default()))
1359    }
1360
1361    pub fn toolkit_version(&self) -> Result<String> {
1362        self.resolve_app_context(|context| Ok(context.toolkit_version.clone().unwrap_or_default()))
1363    }
1364
1365    pub fn id(&self) -> Result<i32> {
1366        self.resolve_app_context(|context| Ok(context.id.unwrap_or(-1)))
1367    }
1368
1369    pub fn set_id(&mut self, id: i32) -> Result<()> {
1370        let app_context = match self.app_context.upgrade() {
1371            Some(context) => context,
1372            None => return Err(Error::Defunct),
1373        };
1374        let mut app_context = app_context.write().unwrap();
1375        app_context.id = Some(id);
1376        Ok(())
1377    }
1378}
1379
1380impl PartialEq for PlatformRoot {
1381    fn eq(&self, other: &Self) -> bool {
1382        self.app_context.ptr_eq(&other.app_context)
1383    }
1384}
1385
1386impl Hash for PlatformRoot {
1387    fn hash<H: Hasher>(&self, state: &mut H) {
1388        self.app_context.as_ptr().hash(state);
1389    }
1390}
1391
1392#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1393pub enum NodeIdOrRoot {
1394    Node(NodeId),
1395    Root,
1396}