Skip to content

Commit db7d4d0

Browse files
fix: Eliminate incorrect removal of reparented nodes (#576)
Co-authored-by: Arnold Loubriat <datatriny@gmail.com>
1 parent fad11a1 commit db7d4d0

File tree

1 file changed

+104
-9
lines changed

1 file changed

+104
-9
lines changed

consumer/src/tree.rs

Lines changed: 104 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ impl State {
4242
mut changes: Option<&mut InternalChanges>,
4343
) {
4444
let mut unreachable = HashSet::new();
45+
let mut seen_child_ids = HashSet::new();
4546

4647
if let Some(tree) = update.tree {
4748
if tree.root != self.data.root {
@@ -74,14 +75,11 @@ impl State {
7475
for (node_id, node_data) in update.nodes {
7576
unreachable.remove(&node_id);
7677

77-
let mut seen_child_ids = HashSet::with_capacity(node_data.children().len());
7878
for (child_index, child_id) in node_data.children().iter().enumerate() {
7979
if seen_child_ids.contains(child_id) {
80-
panic!(
81-
"Node {:?} of TreeUpdate includes duplicate child {:?};",
82-
node_id, child_id
83-
);
80+
panic!("TreeUpdate includes duplicate child {:?}", child_id);
8481
}
82+
seen_child_ids.insert(*child_id);
8583
unreachable.remove(child_id);
8684
let parent_and_index = ParentAndIndex(node_id, child_index);
8785
if let Some(child_state) = self.nodes.get_mut(child_id) {
@@ -102,7 +100,6 @@ impl State {
102100
} else {
103101
pending_children.insert(*child_id, parent_and_index);
104102
}
105-
seen_child_ids.insert(*child_id);
106103
}
107104

108105
if let Some(node_state) = self.nodes.get_mut(&node_id) {
@@ -149,19 +146,22 @@ impl State {
149146
fn traverse_unreachable(
150147
nodes: &mut HashMap<NodeId, NodeState>,
151148
changes: &mut Option<&mut InternalChanges>,
149+
seen_child_ids: &HashSet<NodeId>,
152150
id: NodeId,
153151
) {
154152
if let Some(changes) = changes {
155153
changes.removed_node_ids.insert(id);
156154
}
157155
let node = nodes.remove(&id).unwrap();
158156
for child_id in node.data.children().iter() {
159-
traverse_unreachable(nodes, changes, *child_id);
157+
if !seen_child_ids.contains(child_id) {
158+
traverse_unreachable(nodes, changes, seen_child_ids, *child_id);
159+
}
160160
}
161161
}
162162

163163
for id in unreachable {
164-
traverse_unreachable(&mut self.nodes, &mut changes, id);
164+
traverse_unreachable(&mut self.nodes, &mut changes, &seen_child_ids, id);
165165
}
166166
}
167167

@@ -373,7 +373,7 @@ impl<T> fmt::Display for ShortNodeList<'_, T> {
373373
#[cfg(test)]
374374
mod tests {
375375
use accesskit::{Node, NodeId, Role, Tree, TreeUpdate};
376-
use alloc::vec;
376+
use alloc::{vec, vec::Vec};
377377

378378
#[test]
379379
fn init_tree_with_root_node() {
@@ -765,4 +765,99 @@ mod tests {
765765
let mut handler = Handler {};
766766
tree.update_and_process_changes(update, &mut handler);
767767
}
768+
769+
#[test]
770+
fn move_node() {
771+
struct Handler {
772+
got_updated_root: bool,
773+
got_updated_child: bool,
774+
got_removed_container: bool,
775+
}
776+
777+
fn unexpected_change() {
778+
panic!("expected only updated root and removed container");
779+
}
780+
781+
impl super::ChangeHandler for Handler {
782+
fn node_added(&mut self, _node: &crate::Node) {
783+
unexpected_change();
784+
}
785+
fn node_updated(&mut self, old_node: &crate::Node, new_node: &crate::Node) {
786+
if new_node.id() == NodeId(0)
787+
&& old_node.child_ids().collect::<Vec<NodeId>>() == vec![NodeId(1)]
788+
&& new_node.child_ids().collect::<Vec<NodeId>>() == vec![NodeId(2)]
789+
{
790+
self.got_updated_root = true;
791+
return;
792+
}
793+
if new_node.id() == NodeId(2)
794+
&& old_node.parent_id() == Some(NodeId(1))
795+
&& new_node.parent_id() == Some(NodeId(0))
796+
{
797+
self.got_updated_child = true;
798+
return;
799+
}
800+
unexpected_change();
801+
}
802+
fn focus_moved(
803+
&mut self,
804+
_old_node: Option<&crate::Node>,
805+
_new_node: Option<&crate::Node>,
806+
) {
807+
unexpected_change();
808+
}
809+
fn node_removed(&mut self, node: &crate::Node) {
810+
if node.id() == NodeId(1) {
811+
self.got_removed_container = true;
812+
return;
813+
}
814+
unexpected_change();
815+
}
816+
}
817+
818+
let mut root = Node::new(Role::Window);
819+
root.set_children([NodeId(1)]);
820+
let mut container = Node::new(Role::GenericContainer);
821+
container.set_children([NodeId(2)]);
822+
let update = TreeUpdate {
823+
nodes: vec![
824+
(NodeId(0), root.clone()),
825+
(NodeId(1), container),
826+
(NodeId(2), Node::new(Role::Button)),
827+
],
828+
tree: Some(Tree::new(NodeId(0))),
829+
focus: NodeId(0),
830+
};
831+
let mut tree = crate::Tree::new(update, false);
832+
root.set_children([NodeId(2)]);
833+
let mut handler = Handler {
834+
got_updated_root: false,
835+
got_updated_child: false,
836+
got_removed_container: false,
837+
};
838+
tree.update_and_process_changes(
839+
TreeUpdate {
840+
nodes: vec![(NodeId(0), root)],
841+
tree: None,
842+
focus: NodeId(0),
843+
},
844+
&mut handler,
845+
);
846+
assert!(handler.got_updated_root);
847+
assert!(handler.got_updated_child);
848+
assert!(handler.got_removed_container);
849+
assert_eq!(
850+
tree.state()
851+
.node_by_id(NodeId(0))
852+
.unwrap()
853+
.child_ids()
854+
.collect::<Vec<NodeId>>(),
855+
vec![NodeId(2)]
856+
);
857+
assert!(tree.state().node_by_id(NodeId(1)).is_none());
858+
assert_eq!(
859+
tree.state().node_by_id(NodeId(2)).unwrap().parent_id(),
860+
Some(NodeId(0))
861+
);
862+
}
768863
}

0 commit comments

Comments
 (0)