Chromium Code Reviews| 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 |