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; |
23 console.log("AutomationNode for desktop is loaded"); | 28 this.root_ = desktop; |
| 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 switch (event.key) { |
28 console.log("1 = go to previous element"); | 34 case '1': |
29 this.moveToPrevious_(); | 35 console.log('1 = go to previous element'); |
30 } else if (event.key === "2") { | 36 this.moveToPrevious_(); |
31 console.log("2 = go to next element"); | 37 break; |
32 this.moveToNext_(); | 38 case '2': |
33 } else if (event.key === "3") { | 39 console.log('2 = go to next element'); |
34 console.log("3 = go to child element"); | 40 this.moveToNext_(); |
35 this.moveToFirstChild_(); | 41 break; |
36 } else if (event.key === "4") { | 42 case '3': |
37 console.log("4 = go to parent element"); | 43 console.log('3 = do default on element'); |
38 this.moveToParent_(); | 44 this.doDefault_(); |
39 } else if (event.key === "5") { | 45 break; |
40 console.log("5 is not yet implemented"); | 46 } |
41 console.log("\n"); | 47 if (debuggingEnabled) { |
42 } | 48 switch (event.key) { |
43 chrome.accessibilityPrivate.setFocusRing([this.node_.location]); | 49 case '6': |
| 50 console.log('6 = go to previous element (debug mode)'); |
| 51 this.debugMoveToPrevious_(); |
| 52 break; |
| 53 case '7': |
| 54 console.log('7 = go to next element (debug mode)'); |
| 55 this.debugMoveToNext_(); |
| 56 break; |
| 57 case '8': |
| 58 console.log('8 = go to child element (debug mode)'); |
| 59 this.debugMoveToFirstChild_(); |
| 60 break; |
| 61 case '9': |
| 62 console.log('9 = go to parent element (debug mode)'); |
| 63 this.debugMoveToParent_(); |
| 64 break; |
| 65 } |
| 66 } |
| 67 if (this.node_) |
| 68 chrome.accessibilityPrivate.setFocusRing([this.node_.location]); |
44 }.bind(this)); | 69 }.bind(this)); |
45 }.bind(this)); | 70 }.bind(this)); |
46 }; | 71 }; |
47 | 72 |
48 SwitchAccess.prototype = { | 73 SwitchAccess.prototype = { |
49 /** | 74 /** |
| 75 * Set this.node_ to the previous interesting node. If no interesting node |
| 76 * comes before this.node_, set this.node_ to the last interesting node. |
| 77 * |
| 78 * @private |
| 79 */ |
| 80 moveToPrevious_: function() { |
| 81 if (this.node_) { |
| 82 let prev = this.getPreviousNode_(this.node_); |
| 83 while (prev && !this.isInteresting_(prev)) { |
| 84 prev = this.getPreviousNode_(prev); |
| 85 } |
| 86 if (prev) { |
| 87 this.node_ = prev; |
| 88 this.printNode_(this.node_); |
| 89 return; |
| 90 } |
| 91 } |
| 92 |
| 93 if (this.root_) { |
| 94 console.log('Reached the first interesting node. Restarting with last.') |
| 95 let prev = this.getYoungestDescendant_(this.root_); |
| 96 while (prev && !this.isInteresting_(prev)) { |
| 97 prev = this.getPreviousNode_(prev); |
| 98 } |
| 99 if (prev) { |
| 100 this.node_ = prev; |
| 101 this.printNode_(this.node_); |
| 102 return; |
| 103 } |
| 104 } |
| 105 |
| 106 console.log('Found no interesting nodes to visit.') |
| 107 }, |
| 108 |
| 109 /** |
| 110 * Set this.node_ to the next interesting node. If no interesting node comes |
| 111 * after this.node_, set this.node_ to the first interesting node. |
| 112 * |
| 113 * @private |
| 114 */ |
| 115 moveToNext_: function() { |
| 116 if (this.node_) { |
| 117 let next = this.getNextNode_(this.node_); |
| 118 while (next && !this.isInteresting_(next)) |
| 119 next = this.getNextNode_(next); |
| 120 if (next) { |
| 121 this.node_ = next; |
| 122 this.printNode_(this.node_); |
| 123 return; |
| 124 } |
| 125 } |
| 126 |
| 127 if (this.root_) { |
| 128 console.log('Reached the last interesting node. Restarting with first.'); |
| 129 let next = this.root_.firstChild; |
| 130 while (next && !this.isInteresting_(next)) |
| 131 next = this.getNextNode_(next); |
| 132 if (next) { |
| 133 this.node_ = next; |
| 134 this.printNode_(this.node_); |
| 135 return; |
| 136 } |
| 137 } |
| 138 |
| 139 console.log('Found no interesting nodes to visit.'); |
| 140 }, |
| 141 |
| 142 /** |
| 143 * Given a flat list of nodes in pre-order, get the node that comes after |
| 144 * |node|. |
| 145 * |
| 146 * @param {!AutomationNode} node |
| 147 * @return {AutomationNode} |
| 148 * @private |
| 149 */ |
| 150 getNextNode_: function(node) { |
| 151 // Check for child. |
| 152 let child = node.firstChild; |
| 153 if (child) |
| 154 return child; |
| 155 |
| 156 // No child. Check for right-sibling. |
| 157 let sibling = node.nextSibling; |
| 158 if (sibling) |
| 159 return sibling; |
| 160 |
| 161 // No right-sibling. Get right-sibling of closest ancestor. |
| 162 let ancestor = node.parent; |
| 163 while (ancestor) { |
| 164 let aunt = ancestor.nextSibling; |
| 165 if (aunt) |
| 166 return aunt; |
| 167 ancestor = ancestor.parent; |
| 168 } |
| 169 |
| 170 // No node found after |node|, so return null. |
| 171 return null; |
| 172 }, |
| 173 |
| 174 /** |
| 175 * Given a flat list of nodes in pre-order, get the node that comes before |
| 176 * |node|. |
| 177 * |
| 178 * @param {!AutomationNode} node |
| 179 * @return {AutomationNode} |
| 180 * @private |
| 181 */ |
| 182 getPreviousNode_: function(node) { |
| 183 // Check for left-sibling. Return its youngest descendant if it has one. |
| 184 // Otherwise, return the sibling. |
| 185 let sibling = node.previousSibling; |
| 186 if (sibling) { |
| 187 let descendant = this.getYoungestDescendant_(sibling); |
| 188 if (descendant) |
| 189 return descendant; |
| 190 return sibling; |
| 191 } |
| 192 |
| 193 // No left-sibling. Check for parent. |
| 194 let parent = node.parent; |
| 195 if (parent) |
| 196 return parent; |
| 197 |
| 198 // No node found before |node|, so return null. |
| 199 return null; |
| 200 }, |
| 201 |
| 202 /** |
| 203 * Get the youngest descendant of |node| if it has one. |
| 204 * |
| 205 * @param {!AutomationNode} node |
| 206 * @return {AutomationNode} |
| 207 * @private |
| 208 */ |
| 209 getYoungestDescendant_: function(node) { |
| 210 if (!node.lastChild) |
| 211 return null; |
| 212 |
| 213 while (node.lastChild) |
| 214 node = node.lastChild; |
| 215 |
| 216 return node; |
| 217 }, |
| 218 |
| 219 /** |
| 220 * Returns true if |node| is interesting. |
| 221 * |
| 222 * @param {!AutomationNode} node |
| 223 * @return {boolean} |
| 224 * @private |
| 225 */ |
| 226 isInteresting_: function(node) { |
| 227 return node.state && node.state.focusable; |
| 228 }, |
| 229 |
| 230 /** |
50 * Move to the previous sibling of this.node_ if it has one. | 231 * Move to the previous sibling of this.node_ if it has one. |
51 */ | 232 * |
52 moveToPrevious_: function() { | 233 * @private |
53 var previous = this.node_.previousSibling; | 234 */ |
| 235 debugMoveToPrevious_: function() { |
| 236 let previous = this.node_.previousSibling; |
54 if (previous) { | 237 if (previous) { |
55 this.node_ = previous; | 238 this.node_ = previous; |
56 this.printDetails_(); | 239 this.printDetails_(); |
57 } else { | 240 } else { |
58 console.log("Node is first of siblings"); | 241 console.log('Node is first of siblings'); |
59 console.log("\n"); | 242 console.log('\n'); |
60 } | 243 } |
61 }, | 244 }, |
62 | 245 |
63 /** | 246 /** |
64 * Move to the next sibling of this.node_ if it has one. | 247 * Move to the next sibling of this.node_ if it has one. |
65 */ | 248 * |
66 moveToNext_: function() { | 249 * @private |
67 var next = this.node_.nextSibling; | 250 */ |
| 251 debugMoveToNext_: function() { |
| 252 let next = this.node_.nextSibling; |
68 if (next) { | 253 if (next) { |
69 this.node_ = next; | 254 this.node_ = next; |
70 this.printDetails_(); | 255 this.printDetails_(); |
71 } else { | 256 } else { |
72 console.log("Node is last of siblings"); | 257 console.log('Node is last of siblings'); |
73 console.log("\n"); | 258 console.log('\n'); |
74 } | 259 } |
75 }, | 260 }, |
76 | 261 |
77 /** | 262 /** |
78 * Move to the first child of this.node_ if it has one. | 263 * Move to the first child of this.node_ if it has one. |
79 */ | 264 * |
80 moveToFirstChild_: function() { | 265 * @private |
81 var child = this.node_.firstChild; | 266 */ |
| 267 debugMoveToFirstChild_: function() { |
| 268 let child = this.node_.firstChild; |
82 if (child) { | 269 if (child) { |
83 this.ancestorList_.push(this.node_); | 270 this.ancestorList_.push(this.node_); |
84 this.node_ = child; | 271 this.node_ = child; |
85 this.printDetails_(); | 272 this.printDetails_(); |
86 } else { | 273 } else { |
87 console.log("Node has no children"); | 274 console.log('Node has no children'); |
88 console.log("\n"); | 275 console.log('\n'); |
89 } | 276 } |
90 }, | 277 }, |
91 | 278 |
92 /** | 279 /** |
93 * Move to the parent of this.node_ if it has one. If it does not have a | 280 * 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 | 281 * 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. | 282 * its neighbors, and we move to an ancestor node. |
| 283 * |
| 284 * @private |
96 */ | 285 */ |
97 moveToParent_: function() { | 286 debugMoveToParent_: function() { |
98 var parent = this.node_.parent; | 287 let parent = this.node_.parent; |
99 if (parent) { | 288 if (parent) { |
100 this.ancestorList_.pop(); | 289 this.ancestorList_.pop(); |
101 this.node_ = parent; | 290 this.node_ = parent; |
102 this.printDetails_(); | 291 this.printDetails_(); |
103 } else if (this.ancestorList_.length === 0) { | 292 } else if (this.ancestorList_.length === 0) { |
104 console.log("Node has no parent"); | 293 console.log('Node has no parent'); |
105 console.log("\n"); | 294 console.log('\n'); |
106 } else { | 295 } else { |
107 console.log( | 296 console.log( |
108 "Node could not find its parent, so moved to recent ancestor"); | 297 'Node could not find its parent, so moved to recent ancestor'); |
109 var ancestor = this.ancestorList_.pop(); | 298 let ancestor = this.ancestorList_.pop(); |
110 this.node_ = ancestor; | 299 this.node_ = ancestor; |
111 this.printDetails_(); | 300 this.printDetails_(); |
112 } | 301 } |
113 }, | 302 }, |
114 | 303 |
115 /** | 304 /** |
| 305 * Perform the default action on the currently selected node. |
| 306 * |
| 307 * @private |
| 308 */ |
| 309 doDefault_: function() { |
| 310 let state = this.node_.state; |
| 311 if (state && state.focusable) |
| 312 console.log('Node was focusable, doing default on it') |
| 313 else if (state) |
| 314 console.log('Node was not focusable, but still doing default'); |
| 315 else |
| 316 console.log('Node has no state, still doing default'); |
| 317 console.log('\n'); |
| 318 this.node_.doDefault(); |
| 319 }, |
| 320 |
| 321 // TODO(elichtenberg): Move print functions to a custom logger class. Only |
| 322 // log when debuggingEnabled is true. |
| 323 /** |
116 * Print out details about the currently selected node and the list of | 324 * Print out details about the currently selected node and the list of |
117 * ancestors. | 325 * ancestors. |
118 * | 326 * |
119 * @private | 327 * @private |
120 */ | 328 */ |
121 printDetails_: function() { | 329 printDetails_: function() { |
122 this.printNode_(this.node_); | 330 this.printNode_(this.node_); |
123 console.log(this.ancestorList_); | 331 console.log(this.ancestorList_); |
124 console.log("\n"); | 332 console.log('\n'); |
125 }, | 333 }, |
126 | 334 |
127 /** | 335 /** |
128 * Print out details about a node. | 336 * Print out details about a node. |
129 * | 337 * |
130 * @param {AutomationNode} node | 338 * @param {AutomationNode} node |
131 * @private | 339 * @private |
132 */ | 340 */ |
133 printNode_: function(node) { | 341 printNode_: function(node) { |
134 if (node) { | 342 if (node) { |
135 console.log("Name = " + node.name); | 343 console.log('Name = ' + node.name); |
136 console.log("Role = " + node.role); | 344 console.log('Role = ' + node.role); |
137 if (!node.parent) { | 345 if (!node.parent) |
138 console.log("At index " + node.indexInParent + ", has no parent"); | 346 console.log('At index ' + node.indexInParent + ', has no parent'); |
139 } else { | 347 else { |
140 var numSiblings = node.parent.children.length; | 348 let numSiblings = node.parent.children.length; |
141 console.log( | 349 console.log( |
142 "At index " + node.indexInParent + ", there are " | 350 'At index ' + node.indexInParent + ', there are ' |
143 + numSiblings + " siblings"); | 351 + numSiblings + ' siblings'); |
144 } | 352 } |
145 console.log("Has " + node.children.length + " children"); | 353 console.log('Has ' + node.children.length + ' children'); |
146 } else { | 354 } else { |
147 console.log("Node is null"); | 355 console.log('Node is null'); |
148 } | 356 } |
149 console.log(node); | 357 console.log(node); |
| 358 console.log('\n'); |
150 } | 359 } |
151 }; | 360 }; |
152 | 361 |
153 new SwitchAccess(); | 362 window.switchAccess = new SwitchAccess(); |
OLD | NEW |