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 let AutomationNode = chrome.automation.AutomationNode; | 5 let AutomationNode = chrome.automation.AutomationNode; |
| 6 | 6 |
| 7 let debuggingEnabled = true; | 7 let debuggingEnabled = true; |
| 8 | |
| 8 /** | 9 /** |
| 9 * @constructor | 10 * @constructor |
| 10 */ | 11 */ |
| 11 let SwitchAccess = function() { | 12 let SwitchAccess = function() { |
| 12 console.log('Switch access is enabled'); | 13 console.log('Switch access is enabled'); |
| 13 | 14 |
| 14 // Currently selected node. | 15 /** |
| 15 /** @private {AutomationNode} */ | 16 * User preferences. |
|
David Tseng
2017/03/24 18:53:11
@type
elichtenberg
2017/03/25 00:02:20
Done.
| |
| 17 */ | |
| 18 this.switchAccessPrefs = null; | |
| 19 | |
| 20 /** | |
| 21 * Currently selected node. | |
| 22 * | |
| 23 * @private {AutomationNode} | |
| 24 */ | |
| 16 this.node_ = null; | 25 this.node_ = null; |
| 17 | 26 |
| 18 // Root node (i.e., the desktop). | 27 /** |
| 19 /** @private {AutomationNode} */ | 28 * Root node (i.e., the desktop). |
| 29 * | |
| 30 * @private {AutomationNode} | |
| 31 */ | |
| 20 this.root_ = null; | 32 this.root_ = null; |
| 21 | 33 |
| 22 // List of nodes to push to / pop from in case this.node_ is lost. | 34 this.init_(); |
| 23 /** @private {!Array<!AutomationNode>} */ | |
| 24 this.ancestorList_ = []; | |
| 25 | |
| 26 chrome.automation.getDesktop(function(desktop) { | |
| 27 this.node_ = desktop; | |
| 28 this.root_ = desktop; | |
| 29 console.log('AutomationNode for desktop is loaded'); | |
| 30 this.printDetails_(); | |
| 31 | |
| 32 document.addEventListener('keyup', function(event) { | |
| 33 switch (event.key) { | |
| 34 case '1': | |
| 35 console.log('1 = go to previous element'); | |
| 36 this.moveToPrevious_(); | |
| 37 break; | |
| 38 case '2': | |
| 39 console.log('2 = go to next element'); | |
| 40 this.moveToNext_(); | |
| 41 break; | |
| 42 case '3': | |
| 43 console.log('3 = do default on element'); | |
| 44 this.doDefault_(); | |
| 45 break; | |
| 46 } | |
| 47 if (debuggingEnabled) { | |
| 48 switch (event.key) { | |
| 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 }.bind(this)); | |
| 70 }.bind(this)); | |
| 71 }; | 35 }; |
| 72 | 36 |
| 73 SwitchAccess.prototype = { | 37 SwitchAccess.prototype = { |
| 74 /** | 38 /** |
| 39 * Set this.node_ and this.root_ to the desktop node, and set up preferences | |
| 40 * and event listeners. | |
| 41 * | |
| 42 * @private | |
| 43 */ | |
| 44 init_: function() { | |
| 45 this.switchAccessPrefs = new SwitchAccessPrefs(); | |
| 46 | |
| 47 chrome.automation.getDesktop(function(desktop) { | |
| 48 this.node_ = desktop; | |
| 49 this.root_ = desktop; | |
| 50 console.log('AutomationNode for desktop is loaded'); | |
| 51 this.printNode_(this.node_); | |
| 52 | |
| 53 document.addEventListener('keyup', function(event) { | |
| 54 switch (event.key) { | |
| 55 case '1': | |
| 56 console.log('1 = go to previous element'); | |
| 57 this.moveToPrevious_(); | |
| 58 break; | |
| 59 case '2': | |
| 60 console.log('2 = go to next element'); | |
| 61 this.moveToNext_(); | |
| 62 break; | |
| 63 case '3': | |
| 64 console.log('3 = do default on element'); | |
| 65 this.doDefault_(); | |
| 66 break; | |
| 67 case '4': | |
| 68 this.showOptionsPage_(); | |
| 69 break; | |
| 70 } | |
| 71 if (debuggingEnabled) { | |
| 72 switch (event.key) { | |
| 73 case '6': | |
| 74 console.log('6 = go to previous element (debug mode)'); | |
| 75 this.debugMoveToPrevious_(); | |
| 76 break; | |
| 77 case '7': | |
| 78 console.log('7 = go to next element (debug mode)'); | |
| 79 this.debugMoveToNext_(); | |
| 80 break; | |
| 81 case '8': | |
| 82 console.log('8 = go to child element (debug mode)'); | |
| 83 this.debugMoveToFirstChild_(); | |
| 84 break; | |
| 85 case '9': | |
| 86 console.log('9 = go to parent element (debug mode)'); | |
| 87 this.debugMoveToParent_(); | |
| 88 break; | |
| 89 } | |
| 90 } | |
| 91 if (this.node_) | |
| 92 chrome.accessibilityPrivate.setFocusRing([this.node_.location]); | |
| 93 }.bind(this)); | |
| 94 }.bind(this)); | |
| 95 | |
| 96 document.addEventListener('prefsUpdate', this.handlePrefsUpdate_); | |
| 97 }, | |
| 98 | |
| 99 /** | |
| 75 * Set this.node_ to the previous interesting node. If no interesting node | 100 * 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. | 101 * comes before this.node_, set this.node_ to the last interesting node. |
| 77 * | 102 * |
| 78 * @private | 103 * @private |
| 79 */ | 104 */ |
| 80 moveToPrevious_: function() { | 105 moveToPrevious_: function() { |
| 81 if (this.node_) { | 106 if (this.node_) { |
| 82 let prev = this.getPreviousNode_(this.node_); | 107 let prev = this.getPreviousNode_(this.node_); |
| 83 while (prev && !this.isInteresting_(prev)) { | 108 while (prev && !this.isInteresting_(prev)) { |
| 84 prev = this.getPreviousNode_(prev); | 109 prev = this.getPreviousNode_(prev); |
| (...skipping 135 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 220 * Returns true if |node| is interesting. | 245 * Returns true if |node| is interesting. |
| 221 * | 246 * |
| 222 * @param {!AutomationNode} node | 247 * @param {!AutomationNode} node |
| 223 * @return {boolean} | 248 * @return {boolean} |
| 224 * @private | 249 * @private |
| 225 */ | 250 */ |
| 226 isInteresting_: function(node) { | 251 isInteresting_: function(node) { |
| 227 return node.state && node.state.focusable; | 252 return node.state && node.state.focusable; |
| 228 }, | 253 }, |
| 229 | 254 |
| 255 | |
| 256 /** | |
| 257 * Perform the default action on the currently selected node. | |
| 258 * | |
| 259 * @private | |
| 260 */ | |
| 261 doDefault_: function() { | |
| 262 let state = this.node_.state; | |
| 263 if (state && state.focusable) | |
| 264 console.log('Node was focusable, doing default on it') | |
| 265 else if (state) | |
| 266 console.log('Node was not focusable, but still doing default'); | |
| 267 else | |
| 268 console.log('Node has no state, still doing default'); | |
| 269 console.log('\n'); | |
| 270 this.node_.doDefault(); | |
| 271 }, | |
| 272 | |
| 273 /** | |
| 274 * Open the options page in a new tab. | |
| 275 * | |
| 276 * @private | |
| 277 */ | |
| 278 showOptionsPage_: function() { | |
| 279 let optionsPage = {url: 'options.html'}; | |
| 280 chrome.tabs.create(optionsPage); | |
| 281 }, | |
| 282 | |
| 283 /** | |
| 284 * Handle a change in user preferences. | |
| 285 * | |
| 286 * @param {!Event} event | |
| 287 * @private | |
| 288 */ | |
| 289 handlePrefsUpdate_: function(event) { | |
| 290 let updatedPrefs = event.detail; | |
| 291 for (let key of Object.keys(updatedPrefs)) { | |
| 292 switch (key) { | |
| 293 case 'enableAutoScan': | |
| 294 console.log('Auto-scan enabled set to: ' + updatedPrefs[key]); | |
| 295 break; | |
| 296 case 'autoScanTime': | |
| 297 console.log( | |
| 298 'Auto-scan time set to: ' + updatedPrefs[key] + " seconds"); | |
| 299 break; | |
| 300 } | |
| 301 } | |
| 302 }, | |
| 303 | |
| 304 // TODO(elichtenberg): Move print functions to a custom logger class. Only | |
| 305 // log when debuggingEnabled is true. | |
| 306 /** | |
| 307 * Print out details about a node. | |
| 308 * | |
| 309 * @param {AutomationNode} node | |
| 310 * @private | |
| 311 */ | |
| 312 printNode_: function(node) { | |
| 313 if (node) { | |
| 314 console.log('Name = ' + node.name); | |
| 315 console.log('Role = ' + node.role); | |
| 316 if (!node.parent) | |
| 317 console.log('At index ' + node.indexInParent + ', has no parent'); | |
| 318 else { | |
| 319 let numSiblings = node.parent.children.length; | |
| 320 console.log( | |
| 321 'At index ' + node.indexInParent + ', there are ' | |
| 322 + numSiblings + ' siblings'); | |
| 323 } | |
| 324 console.log('Has ' + node.children.length + ' children'); | |
| 325 } else { | |
| 326 console.log('Node is null'); | |
| 327 } | |
| 328 console.log(node); | |
| 329 console.log('\n'); | |
| 330 }, | |
| 331 | |
| 230 /** | 332 /** |
| 231 * Move to the previous sibling of this.node_ if it has one. | 333 * Move to the previous sibling of this.node_ if it has one. |
| 232 * | 334 * |
| 233 * @private | 335 * @private |
| 234 */ | 336 */ |
| 235 debugMoveToPrevious_: function() { | 337 debugMoveToPrevious_: function() { |
| 236 let previous = this.node_.previousSibling; | 338 let previous = this.node_.previousSibling; |
| 237 if (previous) { | 339 if (previous) { |
| 238 this.node_ = previous; | 340 this.node_ = previous; |
| 239 this.printDetails_(); | 341 this.printNode_(this.node_); |
| 240 } else { | 342 } else { |
| 241 console.log('Node is first of siblings'); | 343 console.log('Node is first of siblings'); |
| 242 console.log('\n'); | 344 console.log('\n'); |
| 243 } | 345 } |
| 244 }, | 346 }, |
| 245 | 347 |
| 246 /** | 348 /** |
| 247 * Move to the next sibling of this.node_ if it has one. | 349 * Move to the next sibling of this.node_ if it has one. |
| 248 * | 350 * |
| 249 * @private | 351 * @private |
| 250 */ | 352 */ |
| 251 debugMoveToNext_: function() { | 353 debugMoveToNext_: function() { |
| 252 let next = this.node_.nextSibling; | 354 let next = this.node_.nextSibling; |
| 253 if (next) { | 355 if (next) { |
| 254 this.node_ = next; | 356 this.node_ = next; |
| 255 this.printDetails_(); | 357 this.printNode_(this.node_); |
| 256 } else { | 358 } else { |
| 257 console.log('Node is last of siblings'); | 359 console.log('Node is last of siblings'); |
| 258 console.log('\n'); | 360 console.log('\n'); |
| 259 } | 361 } |
| 260 }, | 362 }, |
| 261 | 363 |
| 262 /** | 364 /** |
| 263 * Move to the first child of this.node_ if it has one. | 365 * Move to the first child of this.node_ if it has one. |
| 264 * | 366 * |
| 265 * @private | 367 * @private |
| 266 */ | 368 */ |
| 267 debugMoveToFirstChild_: function() { | 369 debugMoveToFirstChild_: function() { |
| 268 let child = this.node_.firstChild; | 370 let child = this.node_.firstChild; |
| 269 if (child) { | 371 if (child) { |
| 270 this.ancestorList_.push(this.node_); | |
| 271 this.node_ = child; | 372 this.node_ = child; |
| 272 this.printDetails_(); | 373 this.printNode_(this.node_); |
| 273 } else { | 374 } else { |
| 274 console.log('Node has no children'); | 375 console.log('Node has no children'); |
| 275 console.log('\n'); | 376 console.log('\n'); |
| 276 } | 377 } |
| 277 }, | 378 }, |
| 278 | 379 |
| 279 /** | 380 /** |
| 280 * Move to the parent of this.node_ if it has one. If it does not have a | 381 * Move to the parent of this.node_ if it has one. |
| 281 * parent but it is not the top level root node, then this.node_ lost track of | |
| 282 * its neighbors, and we move to an ancestor node. | |
| 283 * | 382 * |
| 284 * @private | 383 * @private |
| 285 */ | 384 */ |
| 286 debugMoveToParent_: function() { | 385 debugMoveToParent_: function() { |
| 287 let parent = this.node_.parent; | 386 let parent = this.node_.parent; |
| 288 if (parent) { | 387 if (parent) { |
| 289 this.ancestorList_.pop(); | |
| 290 this.node_ = parent; | 388 this.node_ = parent; |
| 291 this.printDetails_(); | 389 this.printNode_(this.node_); |
| 292 } else if (this.ancestorList_.length === 0) { | 390 } else { |
| 293 console.log('Node has no parent'); | 391 console.log('Node has no parent'); |
| 294 console.log('\n'); | 392 console.log('\n'); |
| 295 } else { | |
| 296 console.log( | |
| 297 'Node could not find its parent, so moved to recent ancestor'); | |
| 298 let ancestor = this.ancestorList_.pop(); | |
| 299 this.node_ = ancestor; | |
| 300 this.printDetails_(); | |
| 301 } | 393 } |
| 302 }, | |
| 303 | |
| 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 /** | |
| 324 * Print out details about the currently selected node and the list of | |
| 325 * ancestors. | |
| 326 * | |
| 327 * @private | |
| 328 */ | |
| 329 printDetails_: function() { | |
| 330 this.printNode_(this.node_); | |
| 331 console.log(this.ancestorList_); | |
| 332 console.log('\n'); | |
| 333 }, | |
| 334 | |
| 335 /** | |
| 336 * Print out details about a node. | |
| 337 * | |
| 338 * @param {AutomationNode} node | |
| 339 * @private | |
| 340 */ | |
| 341 printNode_: function(node) { | |
| 342 if (node) { | |
| 343 console.log('Name = ' + node.name); | |
| 344 console.log('Role = ' + node.role); | |
| 345 if (!node.parent) | |
| 346 console.log('At index ' + node.indexInParent + ', has no parent'); | |
| 347 else { | |
| 348 let numSiblings = node.parent.children.length; | |
| 349 console.log( | |
| 350 'At index ' + node.indexInParent + ', there are ' | |
| 351 + numSiblings + ' siblings'); | |
| 352 } | |
| 353 console.log('Has ' + node.children.length + ' children'); | |
| 354 } else { | |
| 355 console.log('Node is null'); | |
| 356 } | |
| 357 console.log(node); | |
| 358 console.log('\n'); | |
| 359 } | 394 } |
| 360 }; | 395 }; |
| 361 | |
| 362 window.switchAccess = new SwitchAccess(); | |
| OLD | NEW |