| Index: ui/accessibility/ax_tree.cc
|
| diff --git a/ui/accessibility/ax_tree.cc b/ui/accessibility/ax_tree.cc
|
| index 73b5c6d69765bcb9c3af06e673ec2d8e242fbc49..0ca9f597e2785a08ebca6fb6648e7aa69d5b1789 100644
|
| --- a/ui/accessibility/ax_tree.cc
|
| +++ b/ui/accessibility/ax_tree.cc
|
| @@ -26,6 +26,8 @@ std::string TreeToStringHelper(AXNode* node, int indent) {
|
|
|
| // Intermediate state to keep track of during a tree update.
|
| struct AXTreeUpdateState {
|
| + AXTreeUpdateState() : new_root(nullptr) {}
|
| +
|
| // During an update, this keeps track of all nodes that have been
|
| // implicitly referenced as part of this update, but haven't been
|
| // updated yet. It's an error if there are any pending nodes at the
|
| @@ -34,6 +36,9 @@ struct AXTreeUpdateState {
|
|
|
| // Keeps track of new nodes created during this update.
|
| std::set<AXNode*> new_nodes;
|
| +
|
| + // The new root in this update, if any.
|
| + AXNode* new_root;
|
| };
|
|
|
| AXTreeDelegate::AXTreeDelegate() {
|
| @@ -60,7 +65,7 @@ AXTree::AXTree(const AXTreeUpdate& initial_state)
|
|
|
| AXTree::~AXTree() {
|
| if (root_)
|
| - DestroyNodeAndSubtree(root_);
|
| + DestroyNodeAndSubtree(root_, nullptr);
|
| }
|
|
|
| void AXTree::SetDelegate(AXTreeDelegate* delegate) {
|
| @@ -84,11 +89,11 @@ bool AXTree::Unserialize(const AXTreeUpdate& update) {
|
| return false;
|
| }
|
| if (node == root_) {
|
| - DestroySubtree(root_);
|
| + DestroySubtree(root_, &update_state);
|
| root_ = NULL;
|
| } else {
|
| for (int i = 0; i < node->child_count(); ++i)
|
| - DestroySubtree(node->ChildAtIndex(i));
|
| + DestroySubtree(node->ChildAtIndex(i), &update_state);
|
| std::vector<AXNode*> children;
|
| node->SwapChildren(children);
|
| update_state.pending_nodes.insert(node);
|
| @@ -156,7 +161,6 @@ bool AXTree::UpdateNode(const AXNodeData& src,
|
| // of the tree is being swapped, or we're out of sync with the source
|
| // and this is a serious error.
|
| AXNode* node = GetFromId(src.id);
|
| - AXNode* new_root = NULL;
|
| if (node) {
|
| update_state->pending_nodes.erase(node);
|
| node->SetData(src);
|
| @@ -166,8 +170,13 @@ bool AXTree::UpdateNode(const AXNodeData& src,
|
| "%d is not in the tree and not the new root", src.id);
|
| return false;
|
| }
|
| - new_root = CreateNode(NULL, src.id, 0);
|
| - node = new_root;
|
| + if (update_state->new_root) {
|
| + error_ = "Tree update contains two new roots";
|
| + return false;
|
| + }
|
| +
|
| + update_state->new_root = CreateNode(NULL, src.id, 0);
|
| + node = update_state->new_root;
|
| update_state->new_nodes.insert(node);
|
| node->SetData(src);
|
| }
|
| @@ -177,9 +186,9 @@ bool AXTree::UpdateNode(const AXNodeData& src,
|
|
|
| // First, delete nodes that used to be children of this node but aren't
|
| // anymore.
|
| - if (!DeleteOldChildren(node, src.child_ids)) {
|
| - if (new_root)
|
| - DestroySubtree(new_root);
|
| + if (!DeleteOldChildren(node, src.child_ids, update_state)) {
|
| + if (update_state->new_root)
|
| + DestroySubtree(update_state->new_root, update_state);
|
| return false;
|
| }
|
|
|
| @@ -194,30 +203,36 @@ bool AXTree::UpdateNode(const AXNodeData& src,
|
| if (src.role == AX_ROLE_ROOT_WEB_AREA &&
|
| (!root_ || root_->id() != src.id)) {
|
| if (root_)
|
| - DestroySubtree(root_);
|
| + DestroySubtree(root_, update_state);
|
| root_ = node;
|
| }
|
|
|
| return success;
|
| }
|
|
|
| -void AXTree::DestroySubtree(AXNode* node) {
|
| +void AXTree::DestroySubtree(AXNode* node,
|
| + AXTreeUpdateState* update_state) {
|
| if (delegate_)
|
| delegate_->OnSubtreeWillBeDeleted(node);
|
| - DestroyNodeAndSubtree(node);
|
| + DestroyNodeAndSubtree(node, update_state);
|
| }
|
|
|
| -void AXTree::DestroyNodeAndSubtree(AXNode* node) {
|
| +void AXTree::DestroyNodeAndSubtree(AXNode* node,
|
| + AXTreeUpdateState* update_state) {
|
| id_map_.erase(node->id());
|
| for (int i = 0; i < node->child_count(); ++i)
|
| - DestroyNodeAndSubtree(node->ChildAtIndex(i));
|
| + DestroyNodeAndSubtree(node->ChildAtIndex(i), update_state);
|
| if (delegate_)
|
| delegate_->OnNodeWillBeDeleted(node);
|
| + if (update_state) {
|
| + update_state->pending_nodes.erase(node);
|
| + }
|
| node->Destroy();
|
| }
|
|
|
| bool AXTree::DeleteOldChildren(AXNode* node,
|
| - const std::vector<int32>& new_child_ids) {
|
| + const std::vector<int32>& new_child_ids,
|
| + AXTreeUpdateState* update_state) {
|
| // Create a set of child ids in |src| for fast lookup, and return false
|
| // if a duplicate is found;
|
| std::set<int32> new_child_id_set;
|
| @@ -235,7 +250,7 @@ bool AXTree::DeleteOldChildren(AXNode* node,
|
| for (size_t i = 0; i < old_children.size(); ++i) {
|
| int old_id = old_children[i]->id();
|
| if (new_child_id_set.find(old_id) == new_child_id_set.end())
|
| - DestroySubtree(old_children[i]);
|
| + DestroySubtree(old_children[i], update_state);
|
| }
|
|
|
| return true;
|
|
|