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

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

Issue 2601333002: Update json_schema_compiler to handle the Automation extension API (Closed)
Patch Set: Address lots of feedback Created 3 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 unified diff | Download patch
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 Role = chrome.automation.RoleType; 18 var Role = chrome.automation.RoleType;
19 var State = chrome.automation.StateType;
19 20
20 /** 21 /**
21 * @constructor 22 * @constructor
22 */ 23 */
23 AutomationPredicate = function() {}; 24 AutomationPredicate = function() {};
24 25
25 /** 26 /**
26 * @typedef {function(!AutomationNode) : boolean} 27 * @typedef {function(!AutomationNode) : boolean}
27 */ 28 */
28 AutomationPredicate.Unary; 29 AutomationPredicate.Unary;
(...skipping 22 matching lines...) Expand all
51 var anyRole = params.anyRole || []; 52 var anyRole = params.anyRole || [];
52 var anyPredicate = params.anyPredicate || []; 53 var anyPredicate = params.anyPredicate || [];
53 return function(node) { 54 return function(node) {
54 return anyRole.some(function(role) { return role == node.role; }) || 55 return anyRole.some(function(role) { return role == node.role; }) ||
55 anyPredicate.some(function(p) { return p(node); }); 56 anyPredicate.some(function(p) { return p(node); });
56 }; 57 };
57 }; 58 };
58 59
59 /** @type {AutomationPredicate.Unary} */ 60 /** @type {AutomationPredicate.Unary} */
60 AutomationPredicate.checkBox = 61 AutomationPredicate.checkBox =
61 AutomationPredicate.roles([Role.checkBox, Role.switch]); 62 AutomationPredicate.roles([Role.CHECK_BOX, Role.SWITCH]);
62 /** @type {AutomationPredicate.Unary} */ 63 /** @type {AutomationPredicate.Unary} */
63 AutomationPredicate.comboBox = AutomationPredicate.roles( 64 AutomationPredicate.comboBox = AutomationPredicate.roles(
64 [Role.comboBox, Role.popUpButton, Role.menuListPopup]); 65 [Role.COMBO_BOX, Role.POP_UP_BUTTON, Role.MENU_LIST_POPUP]);
65 /** @type {AutomationPredicate.Unary} */ 66 /** @type {AutomationPredicate.Unary} */
66 AutomationPredicate.heading = AutomationPredicate.roles([Role.heading]); 67 AutomationPredicate.heading = AutomationPredicate.roles([Role.HEADING]);
67 /** @type {AutomationPredicate.Unary} */ 68 /** @type {AutomationPredicate.Unary} */
68 AutomationPredicate.inlineTextBox = 69 AutomationPredicate.inlineTextBox =
69 AutomationPredicate.roles([Role.inlineTextBox]); 70 AutomationPredicate.roles([Role.INLINE_TEXT_BOX]);
70 /** @type {AutomationPredicate.Unary} */ 71 /** @type {AutomationPredicate.Unary} */
71 AutomationPredicate.link = AutomationPredicate.roles([Role.link]); 72 AutomationPredicate.link = AutomationPredicate.roles([Role.LINK]);
72 /** @type {AutomationPredicate.Unary} */ 73 /** @type {AutomationPredicate.Unary} */
73 AutomationPredicate.row = AutomationPredicate.roles([Role.row]); 74 AutomationPredicate.row = AutomationPredicate.roles([Role.ROW]);
74 /** @type {AutomationPredicate.Unary} */ 75 /** @type {AutomationPredicate.Unary} */
75 AutomationPredicate.table = AutomationPredicate.roles([Role.grid, Role.table]); 76 AutomationPredicate.table = AutomationPredicate.roles([Role.GRID, Role.TABLE]);
76 77
77 /** 78 /**
78 * @param {!AutomationNode} node 79 * @param {!AutomationNode} node
79 * @return {boolean} 80 * @return {boolean}
80 */ 81 */
81 AutomationPredicate.button = function(node) { 82 AutomationPredicate.button = function(node) {
82 return /button/i.test(node.role); 83 return /button/i.test(node.role);
83 }; 84 };
84 85
85 /** 86 /**
86 * @param {!AutomationNode} node 87 * @param {!AutomationNode} node
87 * @return {boolean} 88 * @return {boolean}
88 */ 89 */
89 AutomationPredicate.editText = function(node) { 90 AutomationPredicate.editText = function(node) {
90 return node.state.editable && 91 return node.state.editable &&
91 node.parent && 92 node.parent &&
92 !node.parent.state.editable; 93 !node.parent.state.editable;
93 }; 94 };
94 95
95 /** @type {AutomationPredicate.Unary} */ 96 /** @type {AutomationPredicate.Unary} */
96 AutomationPredicate.formField = AutomationPredicate.match({ 97 AutomationPredicate.formField = AutomationPredicate.match({
97 anyPredicate: [ 98 anyPredicate: [
98 AutomationPredicate.button, 99 AutomationPredicate.button,
99 AutomationPredicate.comboBox, 100 AutomationPredicate.comboBox,
100 AutomationPredicate.editText 101 AutomationPredicate.editText
101 ], 102 ],
102 anyRole: [ 103 anyRole: [
103 Role.checkBox, 104 Role.CHECK_BOX,
104 Role.colorWell, 105 Role.COLOR_WELL,
105 Role.listBox, 106 Role.LIST_BOX,
106 Role.slider, 107 Role.SLIDER,
107 Role.switch, 108 Role.SWITCH,
108 Role.tab, 109 Role.TAB,
109 Role.tree 110 Role.TREE
110 ] 111 ]
111 }); 112 });
112 113
113 /** @type {AutomationPredicate.Unary} */ 114 /** @type {AutomationPredicate.Unary} */
114 AutomationPredicate.control = AutomationPredicate.match({ 115 AutomationPredicate.control = AutomationPredicate.match({
115 anyPredicate: [ 116 anyPredicate: [
116 AutomationPredicate.formField, 117 AutomationPredicate.formField,
117 ], 118 ],
118 anyRole: [ 119 anyRole: [
119 Role.disclosureTriangle, 120 Role.DISCLOSURE_TRIANGLE,
120 Role.menuItem, 121 Role.MENU_ITEM,
121 Role.menuItemCheckBox, 122 Role.MENU_ITEM_CHECK_BOX,
122 Role.menuItemRadio, 123 Role.MENU_ITEM_RADIO,
123 Role.menuListOption, 124 Role.MENU_LIST_OPTION,
124 Role.scrollBar 125 Role.SCROLL_BAR
125 ] 126 ]
126 }); 127 });
127 128
128 /** 129 /**
129 * @param {!AutomationNode} node 130 * @param {!AutomationNode} node
130 * @return {boolean} 131 * @return {boolean}
131 */ 132 */
132 AutomationPredicate.image = function(node) { 133 AutomationPredicate.image = function(node) {
133 return node.role == Role.image && !!(node.name || node.url); 134 return node.role == Role.IMAGE && !!(node.name || node.url);
134 }; 135 };
135 136
136 /** @type {AutomationPredicate.Unary} */ 137 /** @type {AutomationPredicate.Unary} */
137 AutomationPredicate.linkOrControl = AutomationPredicate.match({ 138 AutomationPredicate.linkOrControl = AutomationPredicate.match({
138 anyPredicate: [ 139 anyPredicate: [
139 AutomationPredicate.control 140 AutomationPredicate.control
140 ], 141 ],
141 anyRole: [ 142 anyRole: [
142 Role.link 143 Role.LINK
143 ] 144 ]
144 }); 145 });
145 146
146 /** @type {AutomationPredicate.Unary} */ 147 /** @type {AutomationPredicate.Unary} */
147 AutomationPredicate.landmark = AutomationPredicate.roles([ 148 AutomationPredicate.landmark = AutomationPredicate.roles([
148 Role.application, 149 Role.APPLICATION,
149 Role.banner, 150 Role.BANNER,
150 Role.complementary, 151 Role.COMPLEMENTARY,
151 Role.contentInfo, 152 Role.CONTENT_INFO,
152 Role.form, 153 Role.FORM,
153 Role.main, 154 Role.MAIN,
154 Role.navigation, 155 Role.NAVIGATION,
155 Role.region, 156 Role.REGION,
156 Role.search]); 157 Role.SEARCH]);
157 158
158 /** 159 /**
159 * @param {!AutomationNode} node 160 * @param {!AutomationNode} node
160 * @return {boolean} 161 * @return {boolean}
161 */ 162 */
162 AutomationPredicate.visitedLink = function(node) { 163 AutomationPredicate.visitedLink = function(node) {
163 return node.state.visited; 164 return node.state.visited;
164 }; 165 };
165 166
166 /** 167 /**
167 * @param {!AutomationNode} node 168 * @param {!AutomationNode} node
168 * @return {boolean} 169 * @return {boolean}
169 */ 170 */
170 AutomationPredicate.focused = function(node) { 171 AutomationPredicate.focused = function(node) {
171 return node.state.focused; 172 return node.state.focused;
172 }; 173 };
173 174
174 /** 175 /**
175 * @param {!AutomationNode} node 176 * @param {!AutomationNode} node
176 * @return {boolean} 177 * @return {boolean}
177 */ 178 */
178 AutomationPredicate.leaf = function(node) { 179 AutomationPredicate.leaf = function(node) {
179 return !node.firstChild || 180 return !node.firstChild ||
180 node.role == Role.button || 181 node.role == Role.BUTTON ||
181 node.role == Role.buttonDropDown || 182 node.role == Role.BUTTON_DROP_DOWN ||
182 node.role == Role.popUpButton || 183 node.role == Role.POP_UP_BUTTON ||
183 node.role == Role.slider || 184 node.role == Role.SLIDER ||
184 node.role == Role.textField || 185 node.role == Role.TEXT_FIELD ||
185 node.state.invisible || 186 node.state[State.INVISIBLE] ||
186 node.children.every(function(n) { 187 node.children.every(function(n) {
187 return n.state.invisible; 188 return n.state[State.INVISIBLE];
188 }); 189 });
189 }; 190 };
190 191
191 /** 192 /**
192 * @param {!AutomationNode} node 193 * @param {!AutomationNode} node
193 * @return {boolean} 194 * @return {boolean}
194 */ 195 */
195 AutomationPredicate.leafWithText = function(node) { 196 AutomationPredicate.leafWithText = function(node) {
196 return AutomationPredicate.leaf(node) && 197 return AutomationPredicate.leaf(node) &&
197 !!(node.name || node.value); 198 !!(node.name || node.value);
198 }; 199 };
199 200
200 /** 201 /**
201 * Matches against leaves or static text nodes. Useful when restricting 202 * Matches against leaves or static text nodes. Useful when restricting
202 * traversal to non-inline textboxes while still allowing them if navigation 203 * traversal to non-inline textboxes while still allowing them if navigation
203 * already entered into an inline textbox. 204 * already entered into an inline textbox.
204 * @param {!AutomationNode} node 205 * @param {!AutomationNode} node
205 * @return {boolean} 206 * @return {boolean}
206 */ 207 */
207 AutomationPredicate.leafOrStaticText = function(node) { 208 AutomationPredicate.leafOrStaticText = function(node) {
208 return AutomationPredicate.leaf(node) || 209 return AutomationPredicate.leaf(node) ||
209 node.role == Role.staticText; 210 node.role == Role.STATIC_TEXT;
210 }; 211 };
211 212
212 /** 213 /**
213 * Matches against nodes visited during object navigation. An object as 214 * Matches against nodes visited during object navigation. An object as
214 * defined below, are all nodes that are focusable or static text. When used in 215 * defined below, are all nodes that are focusable or static text. When used in
215 * tree walking, it should visit all nodes that tab traversal would as well as 216 * tree walking, it should visit all nodes that tab traversal would as well as
216 * non-focusable static text. 217 * non-focusable static text.
217 * @param {!AutomationNode} node 218 * @param {!AutomationNode} node
218 * @return {boolean} 219 * @return {boolean}
219 */ 220 */
220 AutomationPredicate.object = function(node) { 221 AutomationPredicate.object = function(node) {
221 // Editable nodes are within a text-like field and don't make sense when 222 // Editable nodes are within a text-like field and don't make sense when
222 // performing object navigation. Users should use line, word, or character 223 // performing object navigation. Users should use line, word, or character
223 // navigation. Only navigate to the top level node. 224 // navigation. Only navigate to the top level node.
224 if (node.parent && node.parent.state.editable) 225 if (node.parent && node.parent.state.editable)
225 return false; 226 return false;
226 227
227 // Descend into large nodes. 228 // Descend into large nodes.
228 if (node.name && node.name.length > constants.OBJECT_MAX_CHARCOUNT) 229 if (node.name && node.name.length > constants.OBJECT_MAX_CHARCOUNT)
229 return false; 230 return false;
230 231
231 return node.state.focusable || 232 return node.state.focusable ||
232 (AutomationPredicate.leafOrStaticText(node) && 233 (AutomationPredicate.leafOrStaticText(node) &&
233 (/\S+/.test(node.name) || 234 (/\S+/.test(node.name) ||
234 (node.role != Role.lineBreak && 235 (node.role != Role.LINE_BREAK &&
235 node.role != Role.staticText && 236 node.role != Role.STATIC_TEXT &&
236 node.role != Role.inlineTextBox))); 237 node.role != Role.INLINE_TEXT_BOX)));
237 }; 238 };
238 239
239 /** 240 /**
240 * Matches against nodes visited during group navigation. An object as 241 * Matches against nodes visited during group navigation. An object as
241 * @param {!AutomationNode} node 242 * @param {!AutomationNode} node
242 * @return {boolean} 243 * @return {boolean}
243 */ 244 */
244 AutomationPredicate.group = AutomationPredicate.match({ 245 AutomationPredicate.group = AutomationPredicate.match({
245 anyRole: [ 246 anyRole: [
246 Role.heading, 247 Role.HEADING,
247 Role.list, 248 Role.LIST,
248 Role.paragraph 249 Role.PARAGRAPH
249 ], 250 ],
250 anyPredicate: [ 251 anyPredicate: [
251 AutomationPredicate.editText, 252 AutomationPredicate.editText,
252 AutomationPredicate.formField, 253 AutomationPredicate.formField,
253 AutomationPredicate.object, 254 AutomationPredicate.object,
254 AutomationPredicate.table 255 AutomationPredicate.table
255 ] 256 ]
256 }); 257 });
257 258
258 /** 259 /**
(...skipping 11 matching lines...) Expand all
270 271
271 /** 272 /**
272 * Matches against a node that contains other interesting nodes. 273 * Matches against a node that contains other interesting nodes.
273 * These nodes should always have their subtrees scanned when navigating. 274 * These nodes should always have their subtrees scanned when navigating.
274 * @param {!AutomationNode} node 275 * @param {!AutomationNode} node
275 * @return {boolean} 276 * @return {boolean}
276 */ 277 */
277 AutomationPredicate.container = function(node) { 278 AutomationPredicate.container = function(node) {
278 return AutomationPredicate.match({ 279 return AutomationPredicate.match({
279 anyRole: [ 280 anyRole: [
280 Role.div, 281 Role.DIV,
281 Role.document, 282 Role.DOCUMENT,
282 Role.group, 283 Role.GROUP,
283 Role.listItem, 284 Role.LIST_ITEM,
284 Role.toolbar, 285 Role.TOOLBAR,
285 Role.window], 286 Role.WINDOW],
286 anyPredicate: [ 287 anyPredicate: [
287 AutomationPredicate.landmark, 288 AutomationPredicate.landmark,
288 AutomationPredicate.structuralContainer, 289 AutomationPredicate.structuralContainer,
289 function(node) { 290 function(node) {
290 // For example, crosh. 291 // For example, crosh.
291 return (node.role == Role.textField && node.state.readOnly); 292 return (node.role == Role.TEXT_FIELD && node.state.readOnly);
292 }, 293 },
293 function(node) { 294 function(node) {
294 return (node.state.editable && 295 return (node.state.editable &&
295 node.parent && 296 node.parent &&
296 !node.parent.state.editable); 297 !node.parent.state.editable);
297 }] 298 }]
298 })(node); 299 })(node);
299 }; 300 };
300 301
301 /** 302 /**
302 * Matches against nodes that contain interesting nodes, but should never be 303 * Matches against nodes that contain interesting nodes, but should never be
303 * visited. 304 * visited.
304 * @param {!AutomationNode} node 305 * @param {!AutomationNode} node
305 * @return {boolean} 306 * @return {boolean}
306 */ 307 */
307 AutomationPredicate.structuralContainer = AutomationPredicate.roles([ 308 AutomationPredicate.structuralContainer = AutomationPredicate.roles([
308 Role.alertDialog, 309 Role.ALERT_DIALOG,
309 Role.dialog, 310 Role.DIALOG,
310 Role.rootWebArea, 311 Role.ROOT_WEB_AREA,
311 Role.webView, 312 Role.WEB_VIEW,
312 Role.window, 313 Role.WINDOW,
313 Role.embeddedObject, 314 Role.EMBEDDED_OBJECT,
314 Role.iframe, 315 Role.IFRAME,
315 Role.iframePresentational]); 316 Role.IFRAME_PRESENTATIONAL]);
316 317
317 /** 318 /**
318 * Returns whether the given node should not be crossed when performing 319 * Returns whether the given node should not be crossed when performing
319 * traversals up the ancestry chain. 320 * traversals up the ancestry chain.
320 * @param {AutomationNode} node 321 * @param {AutomationNode} node
321 * @return {boolean} 322 * @return {boolean}
322 */ 323 */
323 AutomationPredicate.root = function(node) { 324 AutomationPredicate.root = function(node) {
324 switch (node.role) { 325 switch (node.role) {
325 case Role.dialog: 326 case Role.DIALOG:
326 case Role.window: 327 case Role.WINDOW:
327 return true; 328 return true;
328 case Role.toolbar: 329 case Role.TOOLBAR:
329 return node.root.role == Role.desktop; 330 return node.root.role == Role.DESKTOP;
330 case Role.rootWebArea: 331 case Role.ROOT_WEB_AREA:
331 return !node.parent || node.parent.root.role == Role.desktop; 332 return !node.parent || node.parent.root.role == Role.DESKTOP;
332 default: 333 default:
333 return false; 334 return false;
334 } 335 }
335 }; 336 };
336 337
337 /** 338 /**
338 * Nodes that should be ignored while traversing the automation tree. For 339 * Nodes that should be ignored while traversing the automation tree. For
339 * example, apply this predicate when moving to the next object. 340 * example, apply this predicate when moving to the next object.
340 * @param {!AutomationNode} node 341 * @param {!AutomationNode} node
341 * @return {boolean} 342 * @return {boolean}
342 */ 343 */
343 AutomationPredicate.shouldIgnoreNode = function(node) { 344 AutomationPredicate.shouldIgnoreNode = function(node) {
344 // Ignore invisible nodes. 345 // Ignore invisible nodes.
345 if (node.state.invisible || 346 if (node.state.invisible ||
346 (node.location.height == 0 && node.location.width == 0)) 347 (node.location.height == 0 && node.location.width == 0))
347 return true; 348 return true;
348 349
349 // Ignore structural containres. 350 // Ignore structural containres.
350 if (AutomationPredicate.structuralContainer(node)) 351 if (AutomationPredicate.structuralContainer(node))
351 return true; 352 return true;
352 353
353 // Ignore list markers since we already announce listitem role. 354 // Ignore list markers since we already announce listitem role.
354 if (node.role == Role.listMarker) 355 if (node.role == Role.LIST_MARKER)
355 return true; 356 return true;
356 357
357 // Don't ignore nodes with names or name-like attribute. 358 // Don't ignore nodes with names or name-like attribute.
358 if (node.name || node.value || node.description || node.url) 359 if (node.name || node.value || node.description || node.url)
359 return false; 360 return false;
360 361
361 // Ignore some roles. 362 // Ignore some roles.
362 return AutomationPredicate.leaf(node) && 363 return AutomationPredicate.leaf(node) &&
363 (AutomationPredicate.roles([Role.client, 364 (AutomationPredicate.roles([Role.CLIENT,
364 Role.column, 365 Role.COLUMN,
365 Role.div, 366 Role.DIV,
366 Role.group, 367 Role.GROUP,
367 Role.image, 368 Role.IMAGE,
368 Role.staticText, 369 Role.STATIC_TEXT,
369 Role.svgRoot, 370 Role.SVG_ROOT,
370 Role.tableHeaderContainer, 371 Role.TABLE_HEADER_CONTAINER,
371 Role.unknown 372 Role.UNKNOWN
372 ])(node)); 373 ])(node));
373 }; 374 };
374 375
375 /** 376 /**
376 * Returns if the node has a meaningful checked state. 377 * Returns if the node has a meaningful checked state.
377 * @param {!AutomationNode} node 378 * @param {!AutomationNode} node
378 * @return {boolean} 379 * @return {boolean}
379 */ 380 */
380 AutomationPredicate.checkable = AutomationPredicate.roles([ 381 AutomationPredicate.checkable = AutomationPredicate.roles([
381 Role.checkBox, 382 Role.CHECK_BOX,
382 Role.radioButton, 383 Role.RADIO_BUTTON,
383 Role.menuItemCheckBox, 384 Role.MENU_ITEM_CHECK_BOX,
384 Role.menuItemRadio, 385 Role.MENU_ITEM_RADIO,
385 Role.treeItem]); 386 Role.TREE_ITEM]);
386 387
387 // Table related predicates. 388 // Table related predicates.
388 /** 389 /**
389 * Returns if the node has a cell like role. 390 * Returns if the node has a cell like role.
390 * @param {!AutomationNode} node 391 * @param {!AutomationNode} node
391 * @return {boolean} 392 * @return {boolean}
392 */ 393 */
393 AutomationPredicate.cellLike = AutomationPredicate.roles([ 394 AutomationPredicate.cellLike = AutomationPredicate.roles([
394 Role.cell, 395 Role.CELL,
395 Role.rowHeader, 396 Role.ROW_HEADER,
396 Role.columnHeader]); 397 Role.COLUMN_HEADER]);
397 398
398 /** 399 /**
399 * Returns a predicate that will match against the directed next cell taking 400 * Returns a predicate that will match against the directed next cell taking
400 * into account the current ancestor cell's position in the table. 401 * into account the current ancestor cell's position in the table.
401 * @param {AutomationNode} start 402 * @param {AutomationNode} start
402 * @param {{dir: (Dir|undefined), 403 * @param {{dir: (Dir|undefined),
403 * row: (boolean|undefined), 404 * row: (boolean|undefined),
404 * col: (boolean|undefined)}} opts 405 * col: (boolean|undefined)}} opts
405 * |dir|, specifies direction for |row or/and |col| movement by one cell. 406 * |dir|, specifies direction for |row or/and |col| movement by one cell.
406 * |dir| defaults to forward. 407 * |dir| defaults to forward.
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after
466 }; 467 };
467 468
468 /** 469 /**
469 * Returns a predicate that will match against a heading with a specific 470 * Returns a predicate that will match against a heading with a specific
470 * hierarchical level. 471 * hierarchical level.
471 * @param {number} level 1-6 472 * @param {number} level 1-6
472 * @return {AutomationPredicate.Unary} 473 * @return {AutomationPredicate.Unary}
473 */ 474 */
474 AutomationPredicate.makeHeadingPredicate = function(level) { 475 AutomationPredicate.makeHeadingPredicate = function(level) {
475 return function(node) { 476 return function(node) {
476 return node.role == Role.heading && node.hierarchicalLevel == level; 477 return node.role == Role.HEADING && node.hierarchicalLevel == level;
477 }; 478 };
478 }; 479 };
479 480
480 }); // goog.scope 481 }); // goog.scope
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698