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