Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1192)

Unified Diff: ui/accessibility/ax_tree_serializer.h

Issue 851853002: It is time. (Closed) Base URL: https://github.com/domokit/mojo.git@master
Patch Set: Trying to reup because the last upload failed. Created 5 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « ui/accessibility/ax_tree.cc ('k') | ui/accessibility/ax_tree_serializer.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: ui/accessibility/ax_tree_serializer.h
diff --git a/ui/accessibility/ax_tree_serializer.h b/ui/accessibility/ax_tree_serializer.h
deleted file mode 100644
index f1cbfcea1053760d218d8686fe310273c6a0f7e5..0000000000000000000000000000000000000000
--- a/ui/accessibility/ax_tree_serializer.h
+++ /dev/null
@@ -1,465 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef UI_ACCESSIBILITY_AX_TREE_SERIALIZER_H_
-#define UI_ACCESSIBILITY_AX_TREE_SERIALIZER_H_
-
-#include <set>
-
-#include "base/containers/hash_tables.h"
-#include "base/logging.h"
-#include "base/stl_util.h"
-#include "ui/accessibility/ax_tree_source.h"
-#include "ui/accessibility/ax_tree_update.h"
-
-namespace ui {
-
-struct ClientTreeNode;
-
-// AXTreeSerializer is a helper class that serializes incremental
-// updates to an AXTreeSource as a AXTreeUpdate struct.
-// These structs can be unserialized by a client object such as an
-// AXTree. An AXTreeSerializer keeps track of the tree of node ids that its
-// client is aware of so that it will never generate an AXTreeUpdate that
-// results in an invalid tree.
-//
-// Every node in the source tree must have an id that's a unique positive
-// integer, the same node must not appear twice.
-//
-// Usage:
-//
-// You must call SerializeChanges() every time a node in the tree changes,
-// and send the generated AXTreeUpdate to the client.
-//
-// If a node is added, call SerializeChanges on its parent.
-// If a node is removed, call SerializeChanges on its parent.
-// If a whole new subtree is added, just call SerializeChanges on its root.
-// If the root of the tree changes, call SerializeChanges on the new root.
-//
-// AXTreeSerializer will avoid re-serializing nodes that do not change.
-// For example, if node 1 has children 2, 3, 4, 5 and then child 2 is
-// removed and a new child 6 is added, the AXTreeSerializer will only
-// update nodes 1 and 6 (and any children of node 6 recursively). It will
-// assume that nodes 3, 4, and 5 are not modified unless you explicitly
-// call SerializeChanges() on them.
-//
-// As long as the source tree has unique ids for every node and no loops,
-// and as long as every update is applied to the client tree, AXTreeSerializer
-// will continue to work. If the source tree makes a change but fails to
-// call SerializeChanges properly, the trees may get out of sync - but
-// because AXTreeSerializer always keeps track of what updates it's sent,
-// it will never send an invalid update and the client tree will not break,
-// it just may not contain all of the changes.
-template<typename AXSourceNode>
-class AXTreeSerializer {
- public:
- explicit AXTreeSerializer(AXTreeSource<AXSourceNode>* tree);
- ~AXTreeSerializer();
-
- // Throw out the internal state that keeps track of the nodes the client
- // knows about. This has the effect that the next update will send the
- // entire tree over because it assumes the client knows nothing.
- void Reset();
-
- // Serialize all changes to |node| and append them to |out_update|.
- void SerializeChanges(AXSourceNode node,
- AXTreeUpdate* out_update);
-
- // Delete the client subtree for this node, ensuring that the subtree
- // is re-serialized.
- void DeleteClientSubtree(AXSourceNode node);
-
- // Only for unit testing. Normally this class relies on getting a call
- // to SerializeChanges() every time the source tree changes. For unit
- // testing, it's convenient to create a static AXTree for the initial
- // state and then call ChangeTreeSourceForTesting and then SerializeChanges
- // to simulate the changes you'd get if a tree changed from the initial
- // state to the second tree's state.
- void ChangeTreeSourceForTesting(AXTreeSource<AXSourceNode>* new_tree);
-
- private:
- // Return the least common ancestor of a node in the source tree
- // and a node in the client tree, or NULL if there is no such node.
- // The least common ancestor is the closest ancestor to |node| (which
- // may be |node| itself) that's in both the source tree and client tree,
- // and for which both the source and client tree agree on their ancestor
- // chain up to the root.
- //
- // Example 1:
- //
- // Client Tree Source tree |
- // 1 1 |
- // / \ / \ |
- // 2 3 2 4 |
- //
- // LCA(source node 2, client node 2) is node 2.
- // LCA(source node 3, client node 4) is node 1.
- //
- // Example 2:
- //
- // Client Tree Source tree |
- // 1 1 |
- // / \ / \ |
- // 2 3 2 3 |
- // / \ / / |
- // 4 7 8 4 |
- // / \ / \ |
- // 5 6 5 6 |
- //
- // LCA(source node 8, client node 7) is node 2.
- // LCA(source node 5, client node 5) is node 1.
- // It's not node 5, because the two trees disagree on the parent of
- // node 4, so the LCA is the first ancestor both trees agree on.
- AXSourceNode LeastCommonAncestor(AXSourceNode node,
- ClientTreeNode* client_node);
-
- // Return the least common ancestor of |node| that's in the client tree.
- // This just walks up the ancestors of |node| until it finds a node that's
- // also in the client tree, and then calls LeastCommonAncestor on the
- // source node and client node.
- AXSourceNode LeastCommonAncestor(AXSourceNode node);
-
- // Walk the subtree rooted at |node| and return true if any nodes that
- // would be updated are being reparented. If so, update |out_lca| to point
- // to the least common ancestor of the previous LCA and the previous
- // parent of the node being reparented.
- bool AnyDescendantWasReparented(AXSourceNode node,
- AXSourceNode* out_lca);
-
- ClientTreeNode* ClientTreeNodeById(int32 id);
-
- // Delete the given client tree node and recursively delete all of its
- // descendants.
- void DeleteClientSubtree(ClientTreeNode* client_node);
-
- // Helper function, called recursively with each new node to serialize.
- void SerializeChangedNodes(AXSourceNode node,
- AXTreeUpdate* out_update);
-
- // The tree source.
- AXTreeSource<AXSourceNode>* tree_;
-
- // Our representation of the client tree.
- ClientTreeNode* client_root_;
-
- // A map from IDs to nodes in the client tree.
- base::hash_map<int32, ClientTreeNode*> client_id_map_;
-};
-
-// In order to keep track of what nodes the client knows about, we keep a
-// representation of the client tree - just IDs and parent/child
-// relationships.
-struct AX_EXPORT ClientTreeNode {
- ClientTreeNode();
- virtual ~ClientTreeNode();
- int32 id;
- ClientTreeNode* parent;
- std::vector<ClientTreeNode*> children;
-};
-
-template<typename AXSourceNode>
-AXTreeSerializer<AXSourceNode>::AXTreeSerializer(
- AXTreeSource<AXSourceNode>* tree)
- : tree_(tree),
- client_root_(NULL) {
-}
-
-template<typename AXSourceNode>
-AXTreeSerializer<AXSourceNode>::~AXTreeSerializer() {
- Reset();
-}
-
-template<typename AXSourceNode>
-void AXTreeSerializer<AXSourceNode>::Reset() {
- if (!client_root_)
- return;
-
- DeleteClientSubtree(client_root_);
- client_id_map_.erase(client_root_->id);
- delete client_root_;
- client_root_ = NULL;
-}
-
-template<typename AXSourceNode>
-void AXTreeSerializer<AXSourceNode>::ChangeTreeSourceForTesting(
- AXTreeSource<AXSourceNode>* new_tree) {
- tree_ = new_tree;
-}
-
-template<typename AXSourceNode>
-AXSourceNode AXTreeSerializer<AXSourceNode>::LeastCommonAncestor(
- AXSourceNode node, ClientTreeNode* client_node) {
- if (!tree_->IsValid(node) || client_node == NULL)
- return tree_->GetNull();
-
- std::vector<AXSourceNode> ancestors;
- while (tree_->IsValid(node)) {
- ancestors.push_back(node);
- node = tree_->GetParent(node);
- }
-
- std::vector<ClientTreeNode*> client_ancestors;
- while (client_node) {
- client_ancestors.push_back(client_node);
- client_node = client_node->parent;
- }
-
- // Start at the root. Keep going until the source ancestor chain and
- // client ancestor chain disagree. The last node before they disagree
- // is the LCA.
- AXSourceNode lca = tree_->GetNull();
- int source_index = static_cast<int>(ancestors.size() - 1);
- int client_index = static_cast<int>(client_ancestors.size() - 1);
- while (source_index >= 0 && client_index >= 0) {
- if (tree_->GetId(ancestors[source_index]) !=
- client_ancestors[client_index]->id) {
- return lca;
- }
- lca = ancestors[source_index];
- source_index--;
- client_index--;
- }
- return lca;
-}
-
-template<typename AXSourceNode>
-AXSourceNode AXTreeSerializer<AXSourceNode>::LeastCommonAncestor(
- AXSourceNode node) {
- // Walk up the tree until the source node's id also exists in the
- // client tree, then call LeastCommonAncestor on those two nodes.
- ClientTreeNode* client_node = ClientTreeNodeById(tree_->GetId(node));
- while (tree_->IsValid(node) && !client_node) {
- node = tree_->GetParent(node);
- if (tree_->IsValid(node))
- client_node = ClientTreeNodeById(tree_->GetId(node));
- }
- return LeastCommonAncestor(node, client_node);
-}
-
-template<typename AXSourceNode>
-bool AXTreeSerializer<AXSourceNode>::AnyDescendantWasReparented(
- AXSourceNode node, AXSourceNode* out_lca) {
- bool result = false;
- int id = tree_->GetId(node);
- std::vector<AXSourceNode> children;
- tree_->GetChildren(node, &children);
- for (size_t i = 0; i < children.size(); ++i) {
- AXSourceNode& child = children[i];
- int child_id = tree_->GetId(child);
- ClientTreeNode* client_child = ClientTreeNodeById(child_id);
- if (client_child) {
- if (!client_child->parent) {
- // If the client child has no parent, it must have been the
- // previous root node, so there is no LCA and we can exit early.
- *out_lca = tree_->GetNull();
- return true;
- } else if (client_child->parent->id != id) {
- // If the client child's parent is not this node, update the LCA
- // and return true (reparenting was found).
- *out_lca = LeastCommonAncestor(*out_lca, client_child);
- result = true;
- } else {
- // This child is already in the client tree, we won't
- // recursively serialize it so we don't need to check this
- // subtree recursively for reparenting.
- continue;
- }
- }
-
- // This is a new child or reparented child, check it recursively.
- if (AnyDescendantWasReparented(child, out_lca))
- result = true;
- }
- return result;
-}
-
-template<typename AXSourceNode>
-ClientTreeNode* AXTreeSerializer<AXSourceNode>::ClientTreeNodeById(int32 id) {
- base::hash_map<int32, ClientTreeNode*>::iterator iter =
- client_id_map_.find(id);
- if (iter != client_id_map_.end())
- return iter->second;
- else
- return NULL;
-}
-
-template<typename AXSourceNode>
-void AXTreeSerializer<AXSourceNode>::SerializeChanges(
- AXSourceNode node,
- AXTreeUpdate* out_update) {
- // If the node isn't in the client tree, we need to serialize starting
- // with the LCA.
- AXSourceNode lca = LeastCommonAncestor(node);
-
- if (client_root_) {
- bool need_delete = false;
- if (tree_->IsValid(lca)) {
- // Check for any reparenting within this subtree - if there is
- // any, we need to delete and reserialize the whole subtree
- // that contains the old and new parents of the reparented node.
- if (AnyDescendantWasReparented(lca, &lca))
- need_delete = true;
- }
-
- if (!tree_->IsValid(lca)) {
- // If there's no LCA, just tell the client to destroy the whole
- // tree and then we'll serialize everything from the new root.
- out_update->node_id_to_clear = client_root_->id;
- Reset();
- } else if (need_delete) {
- // Otherwise, if we need to reserialize a subtree, first we need
- // to delete those nodes in our client tree so that
- // SerializeChangedNodes() will be sure to send them again.
- out_update->node_id_to_clear = tree_->GetId(lca);
- ClientTreeNode* client_lca = ClientTreeNodeById(tree_->GetId(lca));
- CHECK(client_lca);
- for (size_t i = 0; i < client_lca->children.size(); ++i) {
- client_id_map_.erase(client_lca->children[i]->id);
- DeleteClientSubtree(client_lca->children[i]);
- delete client_lca->children[i];
- }
- client_lca->children.clear();
- }
- }
-
- // Serialize from the LCA, or from the root if there isn't one.
- if (!tree_->IsValid(lca))
- lca = tree_->GetRoot();
- SerializeChangedNodes(lca, out_update);
-}
-
-template<typename AXSourceNode>
-void AXTreeSerializer<AXSourceNode>::DeleteClientSubtree(AXSourceNode node) {
- ClientTreeNode* client_node = ClientTreeNodeById(tree_->GetId(node));
- if (client_node)
- DeleteClientSubtree(client_node);
-}
-
-template<typename AXSourceNode>
-void AXTreeSerializer<AXSourceNode>::DeleteClientSubtree(
- ClientTreeNode* client_node) {
- for (size_t i = 0; i < client_node->children.size(); ++i) {
- client_id_map_.erase(client_node->children[i]->id);
- DeleteClientSubtree(client_node->children[i]);
- delete client_node->children[i];
- }
- client_node->children.clear();
-}
-
-template<typename AXSourceNode>
-void AXTreeSerializer<AXSourceNode>::SerializeChangedNodes(
- AXSourceNode node,
- AXTreeUpdate* out_update) {
- // This method has three responsibilities:
- // 1. Serialize |node| into an AXNodeData, and append it to
- // the AXTreeUpdate to be sent to the client.
- // 2. Determine if |node| has any new children that the client doesn't
- // know about yet, and call SerializeChangedNodes recursively on those.
- // 3. Update our internal data structure that keeps track of what nodes
- // the client knows about.
-
- // First, find the ClientTreeNode for this id in our data structure where
- // we keep track of what accessibility objects the client already knows
- // about. If we don't find it, then this must be the new root of the
- // accessibility tree.
- int id = tree_->GetId(node);
- ClientTreeNode* client_node = ClientTreeNodeById(id);
- if (!client_node) {
- Reset();
- client_root_ = new ClientTreeNode();
- client_node = client_root_;
- client_node->id = id;
- client_node->parent = NULL;
- client_id_map_[client_node->id] = client_node;
- }
-
- // Iterate over the ids of the children of |node|.
- // Create a set of the child ids so we can quickly look
- // up which children are new and which ones were there before.
- base::hash_set<int32> new_child_ids;
- std::vector<AXSourceNode> children;
- tree_->GetChildren(node, &children);
- for (size_t i = 0; i < children.size(); ++i) {
- AXSourceNode& child = children[i];
- int new_child_id = tree_->GetId(child);
- new_child_ids.insert(new_child_id);
-
- // This is a sanity check - there shouldn't be any reparenting
- // because we've already handled it above.
- ClientTreeNode* client_child = client_id_map_[new_child_id];
- CHECK(!client_child || client_child->parent == client_node);
- }
-
- // Go through the old children and delete subtrees for child
- // ids that are no longer present, and create a map from
- // id to ClientTreeNode for the rest. It's important to delete
- // first in a separate pass so that nodes that are reparented
- // don't end up children of two different parents in the middle
- // of an update, which can lead to a double-free.
- base::hash_map<int32, ClientTreeNode*> client_child_id_map;
- std::vector<ClientTreeNode*> old_children;
- old_children.swap(client_node->children);
- for (size_t i = 0; i < old_children.size(); ++i) {
- ClientTreeNode* old_child = old_children[i];
- int old_child_id = old_child->id;
- if (new_child_ids.find(old_child_id) == new_child_ids.end()) {
- client_id_map_.erase(old_child_id);
- DeleteClientSubtree(old_child);
- delete old_child;
- } else {
- client_child_id_map[old_child_id] = old_child;
- }
- }
-
- // Serialize this node. This fills in all of the fields in
- // AXNodeData except child_ids, which we handle below.
- out_update->nodes.push_back(AXNodeData());
- AXNodeData* serialized_node = &out_update->nodes.back();
- tree_->SerializeNode(node, serialized_node);
- // TODO(dmazzoni/dtseng): Make the serializer not depend on roles to identify
- // the root.
- if (serialized_node->id == client_root_->id &&
- (serialized_node->role != AX_ROLE_ROOT_WEB_AREA &&
- serialized_node->role != AX_ROLE_DESKTOP)) {
- serialized_node->role = AX_ROLE_ROOT_WEB_AREA;
- }
- serialized_node->child_ids.clear();
-
- // Iterate over the children, make note of the ones that are new
- // and need to be serialized, and update the ClientTreeNode
- // data structure to reflect the new tree.
- std::vector<AXSourceNode> children_to_serialize;
- client_node->children.reserve(children.size());
- for (size_t i = 0; i < children.size(); ++i) {
- AXSourceNode& child = children[i];
- int child_id = tree_->GetId(child);
-
- // No need to do anything more with children that aren't new;
- // the client will reuse its existing object.
- if (new_child_ids.find(child_id) == new_child_ids.end())
- continue;
-
- new_child_ids.erase(child_id);
- serialized_node->child_ids.push_back(child_id);
- if (client_child_id_map.find(child_id) != client_child_id_map.end()) {
- ClientTreeNode* reused_child = client_child_id_map[child_id];
- client_node->children.push_back(reused_child);
- } else {
- ClientTreeNode* new_child = new ClientTreeNode();
- new_child->id = child_id;
- new_child->parent = client_node;
- client_node->children.push_back(new_child);
- client_id_map_[child_id] = new_child;
- children_to_serialize.push_back(child);
- }
- }
-
- // Serialize all of the new children, recursively.
- for (size_t i = 0; i < children_to_serialize.size(); ++i)
- SerializeChangedNodes(children_to_serialize[i], out_update);
-}
-
-} // namespace ui
-
-#endif // UI_ACCESSIBILITY_AX_TREE_SERIALIZER_H_
« no previous file with comments | « ui/accessibility/ax_tree.cc ('k') | ui/accessibility/ax_tree_serializer.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698