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