OLD | NEW |
---|---|
1 // Copyright 2017 The Chromium Authors. All rights reserved. | 1 // Copyright 2017 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 var AutomationNode = chrome.automation.AutomationNode; | 5 let AutomationNode = chrome.automation.AutomationNode; |
6 | 6 |
7 let debuggingEnabled = true; | |
7 /** | 8 /** |
8 * @constructor | 9 * @constructor |
9 */ | 10 */ |
10 var SwitchAccess = function() { | 11 let SwitchAccess = function() { |
11 console.log("Switch access is enabled"); | 12 console.log("Switch access is enabled"); |
12 | 13 |
13 // Currently selected node. | 14 // Currently selected node. |
14 /** @private {AutomationNode} */ | 15 /** @private {AutomationNode} */ |
15 this.node_ = null; | 16 this.node_ = null; |
16 | 17 |
18 // Root node (i.e., the desktop). | |
19 /** @private {AutomationNode} */ | |
20 this.root_ = null; | |
21 | |
17 // List of nodes to push to / pop from in case this.node_ is lost. | 22 // List of nodes to push to / pop from in case this.node_ is lost. |
18 /** @private {!Array<!AutomationNode>} */ | 23 /** @private {!Array<!AutomationNode>} */ |
19 this.ancestorList_ = []; | 24 this.ancestorList_ = []; |
20 | 25 |
21 chrome.automation.getDesktop(function(desktop) { | 26 chrome.automation.getDesktop(function(desktop) { |
22 this.node_ = desktop; | 27 this.node_ = desktop; |
28 this.root_ = desktop; | |
23 console.log("AutomationNode for desktop is loaded"); | 29 console.log("AutomationNode for desktop is loaded"); |
24 this.printDetails_(); | 30 this.printDetails_(); |
25 | 31 |
26 document.addEventListener("keyup", function(event) { | 32 document.addEventListener("keyup", function(event) { |
27 if (event.key === "1") { | 33 if (event.key === "1") { |
dmazzoni
2017/03/09 00:00:01
Use a switch instead of several if statements
May
elichtenberg
2017/03/09 19:31:44
Rather than having two separate switch statements
| |
28 console.log("1 = go to previous element"); | 34 console.log("1 = go to previous element"); |
29 this.moveToPrevious_(); | 35 this.moveToPrevious_(); |
30 } else if (event.key === "2") { | 36 } else if (event.key === "2") { |
31 console.log("2 = go to next element"); | 37 console.log("2 = go to next element"); |
32 this.moveToNext_(); | 38 this.moveToNext_(); |
33 } else if (event.key === "3") { | 39 } else if (event.key === "3") { |
34 console.log("3 = go to child element"); | 40 console.log("3 = do default on element"); |
35 this.moveToFirstChild_(); | 41 this.doDefault_(); |
36 } else if (event.key === "4") { | 42 } else if (debuggingEnabled) { |
37 console.log("4 = go to parent element"); | 43 if (event.key === "6") { |
38 this.moveToParent_(); | 44 console.log("6 = go to previous element (debug mode)"); |
39 } else if (event.key === "5") { | 45 this.debugMoveToPrevious_(); |
40 console.log("5 is not yet implemented"); | 46 } else if (event.key === "7") { |
41 console.log("\n"); | 47 console.log("7 = go to next element (debug mode)"); |
42 } | 48 this.debugMoveToNext_(); |
43 chrome.accessibilityPrivate.setFocusRing([this.node_.location]); | 49 } else if (event.key === "8") { |
50 console.log("8 = go to child element (debug mode)"); | |
51 this.debugMoveToFirstChild_(); | |
52 } else if (event.key === "9") { | |
53 console.log("9 = go to parent element (debug mode)"); | |
54 this.debugMoveToParent_(); | |
55 } | |
56 } | |
57 if (this.node_) { | |
58 chrome.accessibilityPrivate.setFocusRing([this.node_.location]); | |
59 } | |
44 }.bind(this)); | 60 }.bind(this)); |
45 }.bind(this)); | 61 }.bind(this)); |
46 }; | 62 }; |
47 | 63 |
48 SwitchAccess.prototype = { | 64 SwitchAccess.prototype = { |
49 /** | 65 /** |
66 * Set this.node_ to the previous interesting node. If no interesting node | |
67 * comes before this.node_, set this.node_ to the last interesting node. | |
68 * | |
69 * @private | |
70 */ | |
71 moveToPrevious_: function() { | |
72 let prev = null; | |
73 if (this.node_) { | |
74 prev = this.getPreviousNode_(this.node_); | |
75 } | |
76 if (prev) { | |
77 this.node_ = prev; | |
78 } else if (this.root_) { | |
79 console.log("Reached the first interesting node. Restarting with last."); | |
80 this.node_ = this.getInterestingDescendant_(this.root_, false); | |
81 } else { | |
82 console.log("Found no interesting nodes to visit"); | |
83 } | |
84 this.printNode_(this.node_); | |
85 console.log("\n"); | |
86 }, | |
87 | |
88 /** | |
89 * Set this.node_ to the next interesting node. If no interesting node comes | |
90 * after this.node_, set this.node_ to the first interesting node. | |
91 * | |
92 * @private | |
93 */ | |
94 moveToNext_: function() { | |
95 let next = null; | |
96 if (this.node_) { | |
97 next = this.getNextNode_(this.node_); | |
98 } | |
99 if (next) { | |
100 this.node_ = next; | |
101 } else if (this.root_) { | |
102 console.log("Reached the last interesting node. Restarting with first."); | |
103 this.node_ = this.getInterestingDescendant_(this.root_, true); | |
104 } else { | |
105 console.log("Found no interesting nodes to visit"); | |
106 } | |
107 this.printNode_(this.node_); | |
108 console.log("\n"); | |
109 }, | |
110 | |
111 /** | |
112 * Given a flat list of nodes in pre-order that are considered interesting, | |
113 * get the node that comes after |node|. | |
114 * | |
115 * @param {!AutomationNode} node | |
116 * @return {AutomationNode} | |
117 * @private | |
118 */ | |
119 getNextNode_: function(node) { | |
120 // Check descendants. | |
121 let descendant = this.getInterestingDescendant_(node, true); | |
122 if (descendant) { | |
123 return descendant; | |
124 } | |
125 | |
126 // No interesting descendant. Check right-siblings and their descendants. | |
127 let siblingOrDescendant = | |
128 this.getInterestingSiblingOrDescendant_(node, false); | |
129 if (siblingOrDescendant) { | |
130 return siblingOrDescendant; | |
131 } | |
132 | |
133 // No interesting right-sibling or descendant of it. Check ancestors and | |
134 // other relatives that come after |node|. | |
135 let ancestorOrRelative = | |
136 this.getInterestingAncestorOrRelative_(node, false); | |
137 if (ancestorOrRelative) { | |
138 return ancestorOrRelative; | |
139 } | |
140 | |
141 // No interesting node found after |node|, so return null. | |
142 return null; | |
143 }, | |
144 | |
145 /** | |
146 * Given a flat list of nodes in pre-order that are considered interesting, | |
147 * get the node that comes before |node|. | |
148 * | |
149 * @param {!AutomationNode} node | |
150 * @return {AutomationNode} | |
151 * @private | |
152 */ | |
153 getPreviousNode_: function(node) { | |
154 // Check left-siblings and their descendants. | |
155 let siblingOrDescendant = | |
156 this.getInterestingSiblingOrDescendant_(node, true); | |
157 if (siblingOrDescendant) { | |
158 return siblingOrDescendant; | |
159 } | |
160 | |
161 // No interesting left-sibling or descendant of it. Check ancestors and | |
162 // other relatives that come before |node|. | |
163 let ancestorOrRelative = this.getInterestingAncestorOrRelative_(node, true); | |
164 if (ancestorOrRelative) { | |
165 return ancestorOrRelative; | |
166 } | |
167 | |
168 // No interesting node found before |node|, so return null. | |
169 return null; | |
170 }, | |
171 | |
172 | |
173 /** | |
174 * Find a descendant of |node| that is considered interesting. | |
175 * If |startAtFirstChild| is true, this function will start by checking the | |
176 * lowest index child, one level below |node|. | |
177 * If |startAtFirstChild| is false, this function will start by checking the | |
178 * leaf descendants of the highest index child. | |
179 * | |
180 * @param {!AutomationNode} node | |
181 * @param {boolean} startAtFirstChild | |
182 * @return {AutomationNode} | |
183 * @private | |
184 */ | |
185 getInterestingDescendant_: function(node, startAtFirstChild) { | |
186 if (startAtFirstChild) { | |
187 let child = node.firstChild; | |
188 while (child) { | |
189 if (this.isInteresting_(child)) { | |
dmazzoni
2017/03/09 00:00:01
Right now you have calls to isInteresting througho
elichtenberg
2017/03/09 19:31:44
Done.
| |
190 return child; | |
191 } | |
192 let descendant = this.getInterestingDescendant_(child, true); | |
193 if (descendant) { | |
194 return descendant; | |
195 } | |
196 child = child.nextSibling; | |
197 } | |
198 } else { | |
199 let child = node.lastChild; | |
200 while (child) { | |
201 let descendant = this.getInterestingDescendant_(child, false); | |
202 if (descendant) { | |
203 return descendant; | |
204 } | |
205 if (this.isInteresting_(child)) { | |
206 return child; | |
207 } | |
208 child = child.previousSibling; | |
209 } | |
210 } | |
211 return null; | |
212 }, | |
213 | |
214 /** | |
215 * Find a sibling or descendant of a sibling of |node| that is considered | |
216 * interesting. | |
217 * If |checkPreviousSibling| is true, this function will only look at |node|'s | |
218 * lower index sibilngs, and it will first check the sibling's descendants. | |
219 * If |checkPreviousSibling| is false, this function will only look at | |
220 * |node|'s higher index siblings, and it will check the sibling before its | |
221 * descendants. | |
222 * | |
223 * @param {!AutomationNode} node | |
224 * @param {boolean} checkPreviousSibling | |
225 * @return {AutomationNode} | |
226 * @private | |
227 */ | |
228 getInterestingSiblingOrDescendant_: function(node, checkPreviousSibling) { | |
229 if (checkPreviousSibling) { | |
230 let sibling = node.previousSibling; | |
231 while (sibling) { | |
232 let descendant = this.getInterestingDescendant_(sibling, false); | |
233 if (descendant) { | |
234 return descendant; | |
235 } | |
236 if (this.isInteresting_(sibling)) { | |
237 return sibling; | |
238 } | |
239 sibling = sibling.previousSibling; | |
240 } | |
241 } else { | |
242 let sibling = node.nextSibling; | |
243 while (sibling) { | |
244 if (this.isInteresting_(sibling)) { | |
245 return sibling; | |
246 } | |
247 let descendant = this.getInterestingDescendant_(sibling, true); | |
248 if (descendant) { | |
249 return descendant; | |
250 } | |
251 sibling = sibling.nextSibling; | |
252 } | |
253 } | |
254 return null; | |
255 }, | |
256 | |
257 /** | |
258 * Find an ancestor or other relative of |node| that is considered | |
259 * interesting. | |
260 * If |findPrevious| is true, will check nodes that come before |node| in | |
261 * pre-order, starting with |node|'s parent. | |
262 * If |findPrevious| is false, will check nodes that come after |node| in | |
263 * pre-order, not including any descendants or direct ancestors. | |
264 * | |
265 * @param {!AutomationNode} node | |
266 * @param {boolean} findPrevious | |
267 * @return {AutomationNode} | |
268 * @private | |
269 */ | |
270 getInterestingAncestorOrRelative_: function(node, findPrevious) { | |
271 let ancestor = node.parent; | |
272 while (ancestor) { | |
273 if (findPrevious && this.isInteresting_(ancestor)) { | |
274 return ancestor; | |
275 } | |
276 let relative = | |
277 this.getInterestingSiblingOrDescendant_(ancestor, findPrevious); | |
278 if (relative) { | |
279 return relative; | |
280 } | |
281 ancestor = ancestor.parent; | |
282 } | |
283 return null; | |
284 }, | |
285 | |
286 /** | |
287 * Returns true if |node| is interesting. | |
288 * | |
289 * @param {!AutomationNode} node | |
290 * @return {boolean} | |
291 * @private | |
292 */ | |
293 isInteresting_: function(node) { | |
294 if (node.state && node.state.focusable) { | |
295 return true; | |
296 } else { | |
297 return false; | |
298 } | |
299 }, | |
300 | |
301 /** | |
50 * Move to the previous sibling of this.node_ if it has one. | 302 * Move to the previous sibling of this.node_ if it has one. |
51 */ | 303 * |
52 moveToPrevious_: function() { | 304 * @private |
53 var previous = this.node_.previousSibling; | 305 */ |
306 debugMoveToPrevious_: function() { | |
307 let previous = this.node_.previousSibling; | |
54 if (previous) { | 308 if (previous) { |
55 this.node_ = previous; | 309 this.node_ = previous; |
56 this.printDetails_(); | 310 this.printDetails_(); |
57 } else { | 311 } else { |
58 console.log("Node is first of siblings"); | 312 console.log("Node is first of siblings"); |
59 console.log("\n"); | 313 console.log("\n"); |
60 } | 314 } |
61 }, | 315 }, |
62 | 316 |
63 /** | 317 /** |
64 * Move to the next sibling of this.node_ if it has one. | 318 * Move to the next sibling of this.node_ if it has one. |
319 * | |
320 * @private | |
65 */ | 321 */ |
66 moveToNext_: function() { | 322 debugMoveToNext_: function() { |
67 var next = this.node_.nextSibling; | 323 let next = this.node_.nextSibling; |
68 if (next) { | 324 if (next) { |
69 this.node_ = next; | 325 this.node_ = next; |
70 this.printDetails_(); | 326 this.printDetails_(); |
71 } else { | 327 } else { |
72 console.log("Node is last of siblings"); | 328 console.log("Node is last of siblings"); |
73 console.log("\n"); | 329 console.log("\n"); |
74 } | 330 } |
75 }, | 331 }, |
76 | 332 |
77 /** | 333 /** |
78 * Move to the first child of this.node_ if it has one. | 334 * Move to the first child of this.node_ if it has one. |
335 * | |
336 * @private | |
79 */ | 337 */ |
80 moveToFirstChild_: function() { | 338 debugMoveToFirstChild_: function() { |
81 var child = this.node_.firstChild; | 339 let child = this.node_.firstChild; |
82 if (child) { | 340 if (child) { |
83 this.ancestorList_.push(this.node_); | 341 this.ancestorList_.push(this.node_); |
84 this.node_ = child; | 342 this.node_ = child; |
85 this.printDetails_(); | 343 this.printDetails_(); |
86 } else { | 344 } else { |
87 console.log("Node has no children"); | 345 console.log("Node has no children"); |
88 console.log("\n"); | 346 console.log("\n"); |
89 } | 347 } |
90 }, | 348 }, |
91 | 349 |
92 /** | 350 /** |
93 * Move to the parent of this.node_ if it has one. If it does not have a | 351 * Move to the parent of this.node_ if it has one. If it does not have a |
94 * parent but it is not the top level root node, then this.node_ lost track of | 352 * parent but it is not the top level root node, then this.node_ lost track of |
95 * its neighbors, and we move to an ancestor node. | 353 * its neighbors, and we move to an ancestor node. |
354 * | |
355 * @private | |
96 */ | 356 */ |
97 moveToParent_: function() { | 357 debugMoveToParent_: function() { |
98 var parent = this.node_.parent; | 358 let parent = this.node_.parent; |
99 if (parent) { | 359 if (parent) { |
100 this.ancestorList_.pop(); | 360 this.ancestorList_.pop(); |
101 this.node_ = parent; | 361 this.node_ = parent; |
102 this.printDetails_(); | 362 this.printDetails_(); |
103 } else if (this.ancestorList_.length === 0) { | 363 } else if (this.ancestorList_.length === 0) { |
104 console.log("Node has no parent"); | 364 console.log("Node has no parent"); |
105 console.log("\n"); | 365 console.log("\n"); |
106 } else { | 366 } else { |
107 console.log( | 367 console.log( |
108 "Node could not find its parent, so moved to recent ancestor"); | 368 "Node could not find its parent, so moved to recent ancestor"); |
109 var ancestor = this.ancestorList_.pop(); | 369 let ancestor = this.ancestorList_.pop(); |
110 this.node_ = ancestor; | 370 this.node_ = ancestor; |
111 this.printDetails_(); | 371 this.printDetails_(); |
112 } | 372 } |
113 }, | 373 }, |
114 | 374 |
115 /** | 375 /** |
376 * Perform the default action on the currently selected node. | |
377 * | |
378 * @private | |
379 */ | |
380 doDefault_: function() { | |
381 let state = this.node_.state; | |
382 if (state && state.focusable) { | |
383 console.log("Node was focusable, doing default on it") | |
384 } else if (state) { | |
385 console.log("Node was not focusable, but still doing default"); | |
386 } else { | |
387 console.log("Node has no state, still doing default"); | |
388 } | |
389 console.log("\n"); | |
390 this.node_.doDefault(); | |
391 }, | |
392 | |
393 /** | |
116 * Print out details about the currently selected node and the list of | 394 * Print out details about the currently selected node and the list of |
117 * ancestors. | 395 * ancestors. |
118 * | 396 * |
119 * @private | 397 * @private |
120 */ | 398 */ |
121 printDetails_: function() { | 399 printDetails_: function() { |
122 this.printNode_(this.node_); | 400 this.printNode_(this.node_); |
123 console.log(this.ancestorList_); | 401 console.log(this.ancestorList_); |
124 console.log("\n"); | 402 console.log("\n"); |
125 }, | 403 }, |
126 | 404 |
127 /** | 405 /** |
128 * Print out details about a node. | 406 * Print out details about a node. |
129 * | 407 * |
130 * @param {AutomationNode} node | 408 * @param {AutomationNode} node |
131 * @private | 409 * @private |
132 */ | 410 */ |
133 printNode_: function(node) { | 411 printNode_: function(node) { |
134 if (node) { | 412 if (node) { |
135 console.log("Name = " + node.name); | 413 console.log("Name = " + node.name); |
136 console.log("Role = " + node.role); | 414 console.log("Role = " + node.role); |
137 if (!node.parent) { | 415 if (!node.parent) { |
138 console.log("At index " + node.indexInParent + ", has no parent"); | 416 console.log("At index " + node.indexInParent + ", has no parent"); |
139 } else { | 417 } else { |
140 var numSiblings = node.parent.children.length; | 418 let numSiblings = node.parent.children.length; |
141 console.log( | 419 console.log( |
142 "At index " + node.indexInParent + ", there are " | 420 "At index " + node.indexInParent + ", there are " |
143 + numSiblings + " siblings"); | 421 + numSiblings + " siblings"); |
144 } | 422 } |
145 console.log("Has " + node.children.length + " children"); | 423 console.log("Has " + node.children.length + " children"); |
146 } else { | 424 } else { |
147 console.log("Node is null"); | 425 console.log("Node is null"); |
148 } | 426 } |
149 console.log(node); | 427 console.log(node); |
150 } | 428 } |
151 }; | 429 }; |
152 | 430 |
153 new SwitchAccess(); | 431 window.switchAccess = new SwitchAccess(); |
OLD | NEW |