1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
6 * @fileoverview ChromeVox utilities for the automation extension API.
9 goog.provide('AutomationUtil');
10 goog.provide('AutomationUtil.Dir');
12 goog.require('AutomationPredicate');
17 AutomationUtil = function() {};
20 * Possible directions to perform tree traversals.
23 AutomationUtil.Dir = {
24 // Search from left to right.
27 // Search from right to left.
32 goog.scope(function() {
33 var AutomationNode = chrome.automation.AutomationNode;
34 var Dir = AutomationUtil.Dir;
37 * Find a node in subtree of |cur| satisfying |pred| using pre-order traversal.
38 * @param {AutomationNode} cur Node to begin the search from.
40 * @param {AutomationPredicate.Unary} pred A predicate to apply
41 * to a candidate node.
42 * @return {AutomationNode}
44 AutomationUtil.findNodePre = function(cur, dir, pred) {
48 var child = dir == Dir.BACKWARD ? cur.lastChild : cur.firstChild;
50 var ret = AutomationUtil.findNodePre(child, dir, pred);
53 child = dir == Dir.BACKWARD ?
54 child.previousSibling : child.nextSibling;
59 * Find a node in subtree of |cur| satisfying |pred| using post-order traversal.
60 * @param {AutomationNode} cur Node to begin the search from.
62 * @param {AutomationPredicate.Unary} pred A predicate to apply
63 * to a candidate node.
64 * @return {AutomationNode}
66 AutomationUtil.findNodePost = function(cur, dir, pred) {
67 var child = dir == Dir.BACKWARD ? cur.lastChild : cur.firstChild;
69 var ret = AutomationUtil.findNodePost(child, dir, pred);
72 child = dir == Dir.BACKWARD ?
73 child.previousSibling : child.nextSibling;
81 * Find the next node in the given direction that is either an immediate sibling
82 * or a sibling of an ancestor.
83 * @param {AutomationNode} cur Node to start search from.
85 * @return {AutomationNode}
87 AutomationUtil.findNextSubtree = function(cur, dir) {
89 var next = dir == Dir.BACKWARD ?
90 cur.previousSibling : cur.nextSibling;
91 if (!AutomationUtil.isInSameTree(cur, next))
95 if (!AutomationUtil.isInSameTree(cur, cur.parent))
102 * Find the next node in the given direction in depth first order.
103 * @param {AutomationNode} cur Node to begin the search from.
105 * @param {AutomationPredicate.Unary} pred A predicate to apply
106 * to a candidate node.
107 * @return {AutomationNode}
109 AutomationUtil.findNextNode = function(cur, dir, pred) {
112 if (!(next = AutomationUtil.findNextSubtree(cur, dir)))
115 next = AutomationUtil.findNodePre(next, dir, pred);
121 * Given nodes a_1, ..., a_n starting at |cur| in pre order traversal, apply
122 * |pred| to a_i and a_(i - 1) until |pred| is satisfied. Returns a_(i - 1) or
123 * a_i (depending on opt_options.before) or null if no match was found.
124 * @param {AutomationNode} cur
126 * @param {AutomationPredicate.Binary} pred
127 * @param {{filter: (AutomationPredicate.Unary|undefined),
128 * before: boolean?}=} opt_options
129 * filter - Filters which candidate nodes to consider. Defaults to leaf
131 * before - True to return a_(i - 1); a_i otherwise. Defaults to false.
132 * @return {AutomationNode}
134 AutomationUtil.findNodeUntil = function(cur, dir, pred, opt_options) {
136 opt_options || {filter: AutomationPredicate.leaf, before: false};
137 if (!opt_options.filter)
138 opt_options.filter = AutomationPredicate.leaf;
143 AutomationUtil.findNextNode(cur,
145 function(candidate) {
146 if (!opt_options.filter(candidate))
149 var satisfied = pred(prev, candidate);
158 return opt_options.before ? before : after;
162 * Returns an array containing ancestors of node starting at root down to node.
163 * @param {!AutomationNode} node
164 * @return {!Array<AutomationNode>}
166 AutomationUtil.getAncestors = function(node) {
168 var candidate = node;
172 if (!AutomationUtil.isInSameTree(candidate, candidate.parent))
175 candidate = candidate.parent;
177 return ret.reverse();
181 * Gets the first index where the two input arrays differ. Returns -1 if they
183 * @param {!Array<AutomationNode>} ancestorsA
184 * @param {!Array<AutomationNode>} ancestorsB
187 AutomationUtil.getDivergence = function(ancestorsA, ancestorsB) {
188 for (var i = 0; i < ancestorsA.length; i++) {
189 if (ancestorsA[i] !== ancestorsB[i])
192 if (ancestorsA.length == ancestorsB.length)
194 return ancestorsA.length;
198 * Returns ancestors of |node| that are not also ancestors of |prevNode|.
199 * @param {!AutomationNode} prevNode
200 * @param {!AutomationNode} node
201 * @return {!Array<AutomationNode>}
203 AutomationUtil.getUniqueAncestors = function(prevNode, node) {
204 var prevAncestors = AutomationUtil.getAncestors(prevNode);
205 var ancestors = AutomationUtil.getAncestors(node);
206 var divergence = AutomationUtil.getDivergence(prevAncestors, ancestors);
207 return ancestors.slice(divergence);
211 * Given |nodeA| and |nodeB| in that order, determines their ordering in the
213 * @param {!AutomationNode} nodeA
214 * @param {!AutomationNode} nodeB
215 * @return {AutomationUtil.Dir}
217 AutomationUtil.getDirection = function(nodeA, nodeB) {
218 var ancestorsA = AutomationUtil.getAncestors(nodeA);
219 var ancestorsB = AutomationUtil.getAncestors(nodeB);
220 var divergence = AutomationUtil.getDivergence(ancestorsA, ancestorsB);
222 // Default to Dir.FORWARD.
223 if (divergence == -1)
226 var divA = ancestorsA[divergence];
227 var divB = ancestorsB[divergence];
229 // One of the nodes is an ancestor of the other. Don't distinguish and just
230 // consider it Dir.FORWARD.
231 if (!divA || !divB || divA.parent === nodeB || divB.parent === nodeA)
234 return divA.indexInParent <= divB.indexInParent ? Dir.FORWARD : Dir.BACKWARD;
238 * Determines whether the two given nodes come from the same tree source.
239 * @param {AutomationNode} a
240 * @param {AutomationNode} b
243 AutomationUtil.isInSameTree = function(a, b) {
247 return a.root === b.root;