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

Side by Side Diff: chrome/browser/resources/chromeos/chromevox/cvox2/background/automation_predicate.js

Issue 2367883002: Clean up AutomationPredicate (Closed)
Patch Set: Remove group from landmark predicate (too noisy...e.g. gmail). Created 4 years, 2 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 unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 /** 5 /**
6 * @fileoverview ChromeVox predicates for the automation extension API. 6 * @fileoverview ChromeVox predicates for the automation extension API.
7 */ 7 */
8 8
9 goog.provide('AutomationPredicate'); 9 goog.provide('AutomationPredicate');
10 goog.provide('AutomationPredicate.Binary'); 10 goog.provide('AutomationPredicate.Binary');
11 goog.provide('AutomationPredicate.Unary'); 11 goog.provide('AutomationPredicate.Unary');
12 12
13 goog.require('constants'); 13 goog.require('constants');
14 14
15 goog.scope(function() { 15 goog.scope(function() {
16 var AutomationNode = chrome.automation.AutomationNode; 16 var AutomationNode = chrome.automation.AutomationNode;
17 var Dir = constants.Dir; 17 var Dir = constants.Dir;
18 var RoleType = chrome.automation.RoleType; 18 var Role = chrome.automation.RoleType;
19 19
20 /** 20 /**
21 * @constructor 21 * @constructor
22 */ 22 */
23 AutomationPredicate = function() {}; 23 AutomationPredicate = function() {};
24 24
25 /** 25 /**
26 * @typedef {function(!AutomationNode) : boolean} 26 * @typedef {function(!AutomationNode) : boolean}
27 */ 27 */
28 AutomationPredicate.Unary; 28 AutomationPredicate.Unary;
29 29
30 /** 30 /**
31 * @typedef {function(!AutomationNode, !AutomationNode) : boolean} 31 * @typedef {function(!AutomationNode, !AutomationNode) : boolean}
32 */ 32 */
33 AutomationPredicate.Binary; 33 AutomationPredicate.Binary;
34 34
35 /** 35 /**
36 * Constructs a predicate given a role. 36 * Constructs a predicate given a list of roles.
37 * @param {RoleType} role 37 * @param {!Array<Role>} roles
38 * @return {AutomationPredicate.Unary} 38 * @return {AutomationPredicate.Unary}
39 */ 39 */
40 AutomationPredicate.withRole = function(role) { 40 AutomationPredicate.roles = function(roles) {
41 return AutomationPredicate.match({anyRole: roles });
42 };
43
44 /**
45 * Constructs a predicate given a list of roles or predicates.
46 * @param {{anyRole: (Array<Role>|undefined),
47 * anyPredicate: (Array<AutomationPredicate.Unary>|undefined)}} params
48 * @return {AutomationPredicate.Unary}
49 */
50 AutomationPredicate.match = function(params) {
51 var anyRole = params.anyRole || [];
52 var anyPredicate = params.anyPredicate || [];
41 return function(node) { 53 return function(node) {
42 return node.role == role; 54 return anyRole.some(function(role) { return role == node.role; }) ||
55 anyPredicate.some(function(p) { return p(node); });
43 }; 56 };
44 }; 57 };
45 58
46 /** @type {AutomationPredicate.Unary} */ 59 /** @type {AutomationPredicate.Unary} */
47 AutomationPredicate.checkBox = AutomationPredicate.withRole(RoleType.checkBox); 60 AutomationPredicate.checkBox = AutomationPredicate.roles([Role.checkBox]);
48 /** @type {AutomationPredicate.Unary} */ 61 /** @type {AutomationPredicate.Unary} */
49 AutomationPredicate.comboBox = AutomationPredicate.withRole(RoleType.comboBox); 62 AutomationPredicate.comboBox = AutomationPredicate.roles(
63 [Role.comboBox, Role.popUpButton, Role.menuListPopup]);
50 /** @type {AutomationPredicate.Unary} */ 64 /** @type {AutomationPredicate.Unary} */
51 AutomationPredicate.heading = AutomationPredicate.withRole(RoleType.heading); 65 AutomationPredicate.heading = AutomationPredicate.roles([Role.heading]);
52 /** @type {AutomationPredicate.Unary} */ 66 /** @type {AutomationPredicate.Unary} */
53 AutomationPredicate.inlineTextBox = 67 AutomationPredicate.inlineTextBox =
54 AutomationPredicate.withRole(RoleType.inlineTextBox); 68 AutomationPredicate.roles([Role.inlineTextBox]);
55 /** @type {AutomationPredicate.Unary} */ 69 /** @type {AutomationPredicate.Unary} */
56 AutomationPredicate.link = AutomationPredicate.withRole(RoleType.link); 70 AutomationPredicate.link = AutomationPredicate.roles([Role.link]);
57 /** @type {AutomationPredicate.Unary} */ 71 /** @type {AutomationPredicate.Unary} */
58 AutomationPredicate.row = AutomationPredicate.withRole(RoleType.row); 72 AutomationPredicate.row = AutomationPredicate.roles([Role.row]);
59 /** @type {AutomationPredicate.Unary} */ 73 /** @type {AutomationPredicate.Unary} */
60 AutomationPredicate.table = AutomationPredicate.withRole(RoleType.table); 74 AutomationPredicate.table = AutomationPredicate.roles([Role.table]);
61 75
62 /** 76 /**
63 * @param {!AutomationNode} node 77 * @param {!AutomationNode} node
64 * @return {boolean} 78 * @return {boolean}
65 */ 79 */
66 AutomationPredicate.button = function(node) { 80 AutomationPredicate.button = function(node) {
67 return /button/i.test(node.role); 81 return /button/i.test(node.role);
68 }; 82 };
69 83
70
71 /** 84 /**
72 * @param {!AutomationNode} node 85 * @param {!AutomationNode} node
73 * @return {boolean} 86 * @return {boolean}
74 */ 87 */
75 AutomationPredicate.editText = function(node) { 88 AutomationPredicate.editText = function(node) {
76 return node.state.editable && 89 return node.state.editable &&
77 node.parent && 90 node.parent &&
78 !node.parent.state.editable; 91 !node.parent.state.editable;
79 }; 92 };
80 93
81 /** 94 /** @type {AutomationPredicate.Unary} */
82 * @param {!AutomationNode} node 95 AutomationPredicate.formField = AutomationPredicate.match({
83 * @return {boolean} 96 anyPredicate: [
84 */ 97 AutomationPredicate.button,
85 AutomationPredicate.formField = function(node) { 98 AutomationPredicate.comboBox,
86 switch (node.role) { 99 AutomationPredicate.editText
87 case 'button': 100 ],
88 case 'buttonDropDown': 101 anyRole: [
89 case 'checkBox': 102 Role.checkBox,
90 case 'comboBox': 103 Role.listBox,
91 case 'date': 104 Role.slider,
92 case 'dateTime': 105 Role.tab,
93 case 'details': 106 Role.tree
94 case 'disclosureTriangle': 107 ]
95 case 'form': 108 });
96 case 'menuButton': 109
97 case 'menuListPopup': 110 /** @type {AutomationPredicate.Unary} */
98 case 'popUpButton': 111 AutomationPredicate.landmark = AutomationPredicate.roles([
99 case 'radioButton': 112 Role.application,
100 case 'searchBox': 113 Role.banner,
101 case 'slider': 114 Role.complementary,
102 case 'spinButton': 115 Role.contentInfo,
103 case 'switch': 116 Role.form,
104 case 'tab': 117 Role.main,
105 case 'textField': 118 Role.navigation,
106 case 'time': 119 Role.region,
107 case 'toggleButton': 120 Role.search]);
108 case 'tree':
109 return true;
110 }
111 return false;
112 };
113 121
114 /** 122 /**
115 * @param {!AutomationNode} node 123 * @param {!AutomationNode} node
116 * @return {boolean}
117 */
118 AutomationPredicate.landmark = function(node) {
119 switch (node.role) {
120 case 'application':
121 case 'banner':
122 case 'complementary':
123 case 'contentInfo':
124 case 'form':
125 case 'main':
126 case 'navigation':
127 case 'search':
128 return true;
129 }
130 return false;
131 };
132
133 /**
134 * @param {!AutomationNode} node
135 * @return {boolean} 124 * @return {boolean}
136 */ 125 */
137 AutomationPredicate.visitedLink = function(node) { 126 AutomationPredicate.visitedLink = function(node) {
138 return node.state.visited; 127 return node.state.visited;
139 }; 128 };
140 129
141 /** 130 /**
142 * @param {!AutomationNode} node 131 * @param {!AutomationNode} node
143 * @return {boolean} 132 * @return {boolean}
144 */ 133 */
145 AutomationPredicate.focused = function(node) { 134 AutomationPredicate.focused = function(node) {
146 return node.state.focused; 135 return node.state.focused;
147 }; 136 };
148 137
149 /** 138 /**
150 * @param {!AutomationNode} node 139 * @param {!AutomationNode} node
151 * @return {boolean} 140 * @return {boolean}
152 */ 141 */
153 AutomationPredicate.leaf = function(node) { 142 AutomationPredicate.leaf = function(node) {
154 return !node.firstChild || 143 return !node.firstChild ||
155 node.role == RoleType.button || 144 node.role == Role.button ||
156 node.role == RoleType.buttonDropDown || 145 node.role == Role.buttonDropDown ||
157 node.role == RoleType.popUpButton || 146 node.role == Role.popUpButton ||
158 node.role == RoleType.slider || 147 node.role == Role.slider ||
159 node.role == RoleType.textField || 148 node.role == Role.textField ||
160 node.state.invisible || 149 node.state.invisible ||
161 node.children.every(function(n) { 150 node.children.every(function(n) {
162 return n.state.invisible; 151 return n.state.invisible;
163 }); 152 });
164 }; 153 };
165 154
166 /** 155 /**
167 * @param {!AutomationNode} node 156 * @param {!AutomationNode} node
168 * @return {boolean} 157 * @return {boolean}
169 */ 158 */
170 AutomationPredicate.leafWithText = function(node) { 159 AutomationPredicate.leafWithText = function(node) {
171 return AutomationPredicate.leaf(node) && 160 return AutomationPredicate.leaf(node) &&
172 !!(node.name || node.value); 161 !!(node.name || node.value);
173 }; 162 };
174 163
175 /** 164 /**
176 * Matches against leaves or static text nodes. Useful when restricting 165 * Matches against leaves or static text nodes. Useful when restricting
177 * traversal to non-inline textboxes while still allowing them if navigation 166 * traversal to non-inline textboxes while still allowing them if navigation
178 * already entered into an inline textbox. 167 * already entered into an inline textbox.
179 * @param {!AutomationNode} node 168 * @param {!AutomationNode} node
180 * @return {boolean} 169 * @return {boolean}
181 */ 170 */
182 AutomationPredicate.leafOrStaticText = function(node) { 171 AutomationPredicate.leafOrStaticText = function(node) {
183 return AutomationPredicate.leaf(node) || 172 return AutomationPredicate.leaf(node) ||
184 node.role == RoleType.staticText; 173 node.role == Role.staticText;
185 }; 174 };
186 175
187 /** 176 /**
188 * Matches against nodes visited during object navigation. An object as 177 * Matches against nodes visited during object navigation. An object as
189 * defined below, are all nodes that are focusable or static text. When used in 178 * defined below, are all nodes that are focusable or static text. When used in
190 * tree walking, it should visit all nodes that tab traversal would as well as 179 * tree walking, it should visit all nodes that tab traversal would as well as
191 * non-focusable static text. 180 * non-focusable static text.
192 * @param {!AutomationNode} node 181 * @param {!AutomationNode} node
193 * @return {boolean} 182 * @return {boolean}
194 */ 183 */
195 AutomationPredicate.object = function(node) { 184 AutomationPredicate.object = function(node) {
196 // Editable nodes are within a text-like field and don't make sense when 185 // Editable nodes are within a text-like field and don't make sense when
197 // performing object navigation. Users should use line, word, or character 186 // performing object navigation. Users should use line, word, or character
198 // navigation. Only navigate to the top level node. 187 // navigation. Only navigate to the top level node.
199 if (node.parent && node.parent.state.editable) 188 if (node.parent && node.parent.state.editable)
200 return false; 189 return false;
201 190
202 return node.state.focusable || 191 return node.state.focusable ||
203 (AutomationPredicate.leafOrStaticText(node) && 192 (AutomationPredicate.leafOrStaticText(node) &&
204 (/\S+/.test(node.name) || 193 (/\S+/.test(node.name) ||
205 (node.role != RoleType.lineBreak && 194 (node.role != Role.lineBreak &&
206 node.role != RoleType.staticText && 195 node.role != Role.staticText &&
207 node.role != RoleType.inlineTextBox))); 196 node.role != Role.inlineTextBox)));
208 }; 197 };
209 198
210 /** 199 /**
211 * @param {!AutomationNode} first 200 * @param {!AutomationNode} first
212 * @param {!AutomationNode} second 201 * @param {!AutomationNode} second
213 * @return {boolean} 202 * @return {boolean}
214 */ 203 */
215 AutomationPredicate.linebreak = function(first, second) { 204 AutomationPredicate.linebreak = function(first, second) {
216 // TODO(dtseng): Use next/previousOnLin once available. 205 // TODO(dtseng): Use next/previousOnLin once available.
217 var fl = first.location; 206 var fl = first.location;
218 var sl = second.location; 207 var sl = second.location;
219 return fl.top != sl.top || 208 return fl.top != sl.top ||
220 (fl.top + fl.height != sl.top + sl.height); 209 (fl.top + fl.height != sl.top + sl.height);
221 }; 210 };
222 211
223 /** 212 /**
224 * Matches against a node that contains other interesting nodes. 213 * Matches against a node that contains other interesting nodes.
225 * These nodes should always have their subtrees scanned when navigating. 214 * These nodes should always have their subtrees scanned when navigating.
226 * @param {!AutomationNode} node 215 * @param {!AutomationNode} node
227 * @return {boolean} 216 * @return {boolean}
228 */ 217 */
229 AutomationPredicate.container = function(node) { 218 AutomationPredicate.container = function(node) {
230 return AutomationPredicate.structuralContainer(node) || 219 return AutomationPredicate.structuralContainer(node) ||
231 node.role == RoleType.div || 220 node.role == Role.div ||
232 node.role == RoleType.document || 221 node.role == Role.document ||
233 node.role == RoleType.group || 222 node.role == Role.group ||
234 node.role == RoleType.listItem || 223 node.role == Role.listItem ||
235 node.role == RoleType.toolbar || 224 node.role == Role.toolbar ||
236 node.role == RoleType.window || 225 node.role == Role.window ||
237 // For example, crosh. 226 // For example, crosh.
238 (node.role == RoleType.textField && node.state.readOnly) || 227 (node.role == Role.textField && node.state.readOnly) ||
239 (node.state.editable && node.parent && !node.parent.state.editable); 228 (node.state.editable && node.parent && !node.parent.state.editable);
240 }; 229 };
241 230
242 /** 231 /**
243 * Matches against nodes that contain interesting nodes, but should never be 232 * Matches against nodes that contain interesting nodes, but should never be
244 * visited. 233 * visited.
245 * @param {!AutomationNode} node 234 * @param {!AutomationNode} node
246 * @return {boolean} 235 * @return {boolean}
247 */ 236 */
248 AutomationPredicate.structuralContainer = function(node) { 237 AutomationPredicate.structuralContainer = function(node) {
249 return node.role == RoleType.rootWebArea || 238 return node.role == Role.rootWebArea ||
250 node.role == RoleType.embeddedObject || 239 node.role == Role.embeddedObject ||
251 node.role == RoleType.iframe || 240 node.role == Role.iframe ||
252 node.role == RoleType.iframePresentational; 241 node.role == Role.iframePresentational;
253 }; 242 };
254 243
255 /** 244 /**
256 * Returns whether the given node should not be crossed when performing 245 * Returns whether the given node should not be crossed when performing
257 * traversals up the ancestry chain. 246 * traversals up the ancestry chain.
258 * @param {AutomationNode} node 247 * @param {AutomationNode} node
259 * @return {boolean} 248 * @return {boolean}
260 */ 249 */
261 AutomationPredicate.root = function(node) { 250 AutomationPredicate.root = function(node) {
262 switch (node.role) { 251 switch (node.role) {
263 case RoleType.dialog: 252 case Role.dialog:
264 case RoleType.window: 253 case Role.window:
265 return true; 254 return true;
266 case RoleType.toolbar: 255 case Role.toolbar:
267 return node.root.role == RoleType.desktop; 256 return node.root.role == Role.desktop;
268 case RoleType.rootWebArea: 257 case Role.rootWebArea:
269 return !node.parent || node.parent.root.role == RoleType.desktop; 258 return !node.parent || node.parent.root.role == Role.desktop;
270 default: 259 default:
271 return false; 260 return false;
272 } 261 }
273 }; 262 };
274 263
275 /** 264 /**
276 * Nodes that should be ignored while traversing the automation tree. For 265 * Nodes that should be ignored while traversing the automation tree. For
277 * example, apply this predicate when moving to the next object. 266 * example, apply this predicate when moving to the next object.
278 * @param {!AutomationNode} node 267 * @param {!AutomationNode} node
279 * @return {boolean} 268 * @return {boolean}
280 */ 269 */
281 AutomationPredicate.shouldIgnoreNode = function(node) { 270 AutomationPredicate.shouldIgnoreNode = function(node) {
282 // Ignore invisible nodes. 271 // Ignore invisible nodes.
283 if (node.state.invisible || 272 if (node.state.invisible ||
284 (node.location.height == 0 && node.location.width == 0)) 273 (node.location.height == 0 && node.location.width == 0))
285 return true; 274 return true;
286 275
287 // Ignore structural containres. 276 // Ignore structural containres.
288 if (AutomationPredicate.structuralContainer(node)) 277 if (AutomationPredicate.structuralContainer(node))
289 return true; 278 return true;
290 279
291 // Ignore list markers since we already announce listitem role. 280 // Ignore list markers since we already announce listitem role.
292 if (node.role == RoleType.listMarker) 281 if (node.role == Role.listMarker)
293 return true; 282 return true;
294 283
295 // Don't ignore nodes with names. 284 // Don't ignore nodes with names.
296 if (node.name || node.value || node.description) 285 if (node.name || node.value || node.description)
297 return false; 286 return false;
298 287
299 // Ignore some roles. 288 // Ignore some roles.
300 return AutomationPredicate.leaf(node) && 289 return AutomationPredicate.leaf(node) &&
301 (node.role == RoleType.client || 290 (AutomationPredicate.roles([Role.client,
302 node.role == RoleType.column || 291 Role.column,
303 node.role == RoleType.div || 292 Role.div,
304 node.role == RoleType.group || 293 Role.group,
305 node.role == RoleType.image || 294 Role.image,
306 node.role == RoleType.staticText || 295 Role.staticText,
307 node.role == RoleType.tableHeaderContainer); 296 Role.tableHeaderContainer])(node));
308 }; 297 };
309 298
310
311 /** 299 /**
312 * Returns if the node has a meaningful checked state. 300 * Returns if the node has a meaningful checked state.
313 * @param {!AutomationNode} node 301 * @param {!AutomationNode} node
314 * @return {boolean} 302 * @return {boolean}
315 */ 303 */
316 AutomationPredicate.checkable = function(node) { 304 AutomationPredicate.checkable = AutomationPredicate.roles([
317 return node.role == RoleType.checkBox || 305 Role.checkBox,
318 node.role == RoleType.radioButton || 306 Role.radioButton,
319 node.role == RoleType.menuItemCheckBox || 307 Role.menuItemCheckBox,
320 node.role == RoleType.menuItemRadio || 308 Role.menuItemRadio,
321 node.role == RoleType.treeItem; 309 Role.treeItem]);
322 };
323 310
324 // Table related predicates. 311 // Table related predicates.
325 /** 312 /**
326 * Returns if the node has a cell like role. 313 * Returns if the node has a cell like role.
327 * @param {!AutomationNode} node 314 * @param {!AutomationNode} node
328 * @return {boolean} 315 * @return {boolean}
329 */ 316 */
330 AutomationPredicate.cellLike = function(node) { 317 AutomationPredicate.cellLike = AutomationPredicate.roles([
331 return node.role == RoleType.cell || 318 Role.cell,
332 node.role == RoleType.rowHeader || 319 Role.rowHeader,
333 node.role == RoleType.columnHeader; 320 Role.columnHeader]);
334 };
335 321
336 /** 322 /**
337 * Returns a predicate that will match against the directed next cell taking 323 * Returns a predicate that will match against the directed next cell taking
338 * into account the current ancestor cell's position in the table. 324 * into account the current ancestor cell's position in the table.
339 * @param {AutomationNode} start 325 * @param {AutomationNode} start
340 * @param {{dir: (Dir|undefined), 326 * @param {{dir: (Dir|undefined),
341 * row: (boolean|undefined), 327 * row: (boolean|undefined),
342 * col: (boolean|undefined)}} opts 328 * col: (boolean|undefined)}} opts
343 * |dir|, specifies direction for |row or/and |col| movement by one cell. 329 * |dir|, specifies direction for |row or/and |col| movement by one cell.
344 * |dir| defaults to forward. 330 * |dir| defaults to forward.
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after
404 }; 390 };
405 391
406 /** 392 /**
407 * Returns a predicate that will match against a heading with a specific 393 * Returns a predicate that will match against a heading with a specific
408 * hierarchical level. 394 * hierarchical level.
409 * @param {number} level 1-6 395 * @param {number} level 1-6
410 * @return {AutomationPredicate.Unary} 396 * @return {AutomationPredicate.Unary}
411 */ 397 */
412 AutomationPredicate.makeHeadingPredicate = function(level) { 398 AutomationPredicate.makeHeadingPredicate = function(level) {
413 return function(node) { 399 return function(node) {
414 return node.role == RoleType.heading && node.hierarchicalLevel == level; 400 return node.role == Role.heading && node.hierarchicalLevel == level;
415 }; 401 };
416 }; 402 };
417 403
418 }); // goog.scope 404 }); // goog.scope
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698