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

Unified Diff: ui/accessibility/ax_tree_serializer.h

Issue 67283004: First step to move common accessibility code out of content. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Git rid of AX_EXPORT from templatized function Created 7 years, 1 month 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
new file mode 100644
index 0000000000000000000000000000000000000000..3ed94e9b445f057ba97f68501dc77251952559f3
--- /dev/null
+++ b/ui/accessibility/ax_tree_serializer.h
@@ -0,0 +1,252 @@
+// Copyright (c) 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 "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 vector of AXNodeData structs.
+// These structs can be unserialized by an AXTree. An AXTreeSerializer
+// keeps track of the tree of node ids that its client is aware of, so
+// it will automatically include, as part of any update, any additional nodes
+// that the client is not aware of yet.
+//
+// When the AXTreeSource changes, call SerializeChanges to serialize the
+// changes to the tree as an AXTreeUpdate. If a single node has changed,
+// pass that node to SerializeChanges. If a node has been added or removed,
+// pass the parent of that node to SerializeChanges and it will automatically
+// handle changes to its set of children.
+//
+// TODO(dmazzoni): add sample usage code.
+template<class AXSourceNode>
+class AXTreeSerializer {
+ public:
+ explicit AXTreeSerializer(AXTreeSource<AXSourceNode>* tree);
+
+ // 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(const AXSourceNode* node,
+ AXTreeUpdate* out_update);
+
+ private:
+ // Delete the given client tree node and recursively delete all of its
+ // descendants.
+ void DeleteClientSubtree(ClientTreeNode* client_node);
+
+ void SerializeChangedNodes(const AXSourceNode* node,
+ std::set<int>* ids_serialized,
+ 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<class AXSourceNode>
+AXTreeSerializer<AXSourceNode>::AXTreeSerializer(
+ AXTreeSource<AXSourceNode>* tree)
+ : tree_(tree),
+ client_root_(NULL) {
+}
+
+template<class AXSourceNode>
+void AXTreeSerializer<AXSourceNode>::Reset() {
+ if (client_root_) {
+ DeleteClientSubtree(client_root_);
+ client_root_ = NULL;
+ }
+}
+
+template<class AXSourceNode>
+void AXTreeSerializer<AXSourceNode>::SerializeChanges(
+ const AXSourceNode* node,
+ AXTreeUpdate* out_update) {
+ std::set<int> ids_serialized;
+ SerializeChangedNodes(node, &ids_serialized, out_update);
+}
+
+template<class 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]);
+ }
+ client_node->children.clear();
+}
+
+template<class AXSourceNode>
+void AXTreeSerializer<AXSourceNode>::SerializeChangedNodes(
+ const AXSourceNode* node,
+ std::set<int>* ids_serialized,
+ AXTreeUpdate* out_update) {
+ int id = tree_->GetId(node);
+ if (ids_serialized->find(id) != ids_serialized->end())
+ return;
+ ids_serialized->insert(id);
+
+ // 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.
+ //
+ // TODO(dmazzoni): handle the case where the root changes.
+ ClientTreeNode* client_node = NULL;
+ base::hash_map<int32, ClientTreeNode*>::iterator iter =
+ client_id_map_.find(id);
+ if (iter != client_id_map_.end()) {
+ client_node = iter->second;
+ } else {
+ if (client_root_) {
+ client_id_map_.erase(client_root_->id);
+ DeleteClientSubtree(client_root_);
+ }
+ 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.
+ // Also catch the case where a child is already in the client tree
+ // data structure with a different parent, and make sure the old parent
+ // clears this node first.
+ base::hash_set<int32> new_child_ids;
+ int child_count = tree_->GetChildCount(node);
+ for (int i = 0; i < child_count; ++i) {
+ AXSourceNode* child = tree_->GetChildAtIndex(node, i);
+ int new_child_id = tree_->GetId(child);
+ new_child_ids.insert(new_child_id);
+
+ ClientTreeNode* client_child = client_id_map_[new_child_id];
+ if (client_child && client_child->parent != client_node) {
+ // The child is being reparented. Find the source tree node
+ // corresponding to the old parent, or the closest ancestor
+ // still in the tree.
+ ClientTreeNode* client_parent = client_child->parent;
+ AXSourceNode* parent = NULL;
+ while (client_parent) {
+ parent = tree_->GetFromId(client_parent->id);
+ if (parent)
+ break;
+ client_parent = client_parent->parent;
+ }
+ CHECK(parent);
+
+ // Call SerializeChangedNodes recursively on the old parent,
+ // so that the update that clears |child| from its old parent
+ // occurs stricly before the update that adds |child| to its
+ // new parent.
+ SerializeChangedNodes(parent, ids_serialized, out_update);
+ }
+ }
+
+ // 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);
+ } 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);
+ if (serialized_node->id == client_root_->id)
+ 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(child_count);
+ for (int i = 0; i < child_count; ++i) {
+ AXSourceNode* child = tree_->GetChildAtIndex(node, 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], ids_serialized, 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