Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(171)

Side by Side Diff: chrome/browser/resources/chromeos/switch_access/switch_access.js

Issue 2777203006: Added auto-scan, made some small changes to how prefs are stored, refactored. (Closed)
Patch Set: Responding to comments. Refactored tree_walker. Removed testable_tree_walker. Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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;
6
7 let debuggingEnabled = true;
8
9 /** 5 /**
6 * Class to manage SwitchAccess and interact with other controllers.
7 *
10 * @constructor 8 * @constructor
9 * @implements {SwitchAccessInterface}
11 */ 10 */
12 let SwitchAccess = function() { 11 function SwitchAccess() {
13 console.log('Switch access is enabled'); 12 console.log('Switch access is enabled');
14 13
15 /** 14 /**
16 * User preferences. 15 * User preferences.
17 * 16 *
18 * @type {SwitchAccessPrefs} 17 * @type {SwitchAccessPrefs}
19 */ 18 */
20 this.switchAccessPrefs = null; 19 this.switchAccessPrefs = null;
21 20
22 /** 21 /**
22 * Handles changes to auto-scan.
23 *
24 * @private {AutoScanManager}
25 */
26 this.autoScanManager_ = null;
27
28 /**
29 * Handles keyboard input.
30 *
31 * @private {KeyboardHandler}
32 */
33 this.keyboardHandler_ = null;
34
35 /**
36 * Moves to the appropriate node in the accessibility tree.
37 *
38 * @private {AutomationTreeWalker}
39 */
40 this.treeWalker_ = null;
41
42 /**
23 * Currently selected node. 43 * Currently selected node.
24 * 44 *
25 * @private {AutomationNode} 45 * @private {chrome.automation.AutomationNode}
26 */ 46 */
27 this.node_ = null; 47 this.node_ = null;
28 48
29 /** 49 /**
30 * Root node (i.e., the desktop). 50 * Root node (i.e., the desktop).
31 * 51 *
32 * @private {AutomationNode} 52 * @private {chrome.automation.AutomationNode}
33 */ 53 */
34 this.root_ = null; 54 this.root_ = null;
35 55
36 this.init_(); 56 this.init_();
37 }; 57 };
38 58
39 SwitchAccess.prototype = { 59 SwitchAccess.prototype = {
40 /** 60 /**
41 * Set this.node_ and this.root_ to the desktop node, and set up preferences 61 * Set this.node_ and this.root_ to the desktop node, and set up preferences,
42 * and event listeners. 62 * controllers, and event listeners.
43 * 63 *
44 * @private 64 * @private
45 */ 65 */
46 init_: function() { 66 init_: function() {
47 this.switchAccessPrefs = new SwitchAccessPrefs(); 67 this.switchAccessPrefs = new SwitchAccessPrefs();
68 this.autoScanManager_ = new AutoScanManager(this);
69 this.keyboardHandler_ = new KeyboardHandler(this);
70 this.treeWalker_ = new AutomationTreeWalker();
48 71
49 chrome.automation.getDesktop(function(desktop) { 72 chrome.automation.getDesktop(function(desktop) {
50 this.node_ = desktop; 73 this.node_ = desktop;
51 this.root_ = desktop; 74 this.root_ = desktop;
52 console.log('AutomationNode for desktop is loaded'); 75 console.log('AutomationNode for desktop is loaded');
53 this.printNode_(this.node_); 76 this.printNode_(this.node_);
54
55 document.addEventListener('keyup', function(event) {
56 switch (event.key) {
57 case '1':
58 console.log('1 = go to previous element');
59 this.moveToPrevious_();
60 break;
61 case '2':
62 console.log('2 = go to next element');
63 this.moveToNext_();
64 break;
65 case '3':
66 console.log('3 = do default on element');
67 this.doDefault_();
68 break;
69 case '4':
70 this.showOptionsPage_();
71 break;
72 }
73 if (debuggingEnabled) {
74 switch (event.key) {
75 case '6':
76 console.log('6 = go to previous element (debug mode)');
77 this.debugMoveToPrevious_();
78 break;
79 case '7':
80 console.log('7 = go to next element (debug mode)');
81 this.debugMoveToNext_();
82 break;
83 case '8':
84 console.log('8 = go to child element (debug mode)');
85 this.debugMoveToFirstChild_();
86 break;
87 case '9':
88 console.log('9 = go to parent element (debug mode)');
89 this.debugMoveToParent_();
90 break;
91 }
92 }
93 if (this.node_)
94 chrome.accessibilityPrivate.setFocusRing([this.node_.location]);
95 }.bind(this));
96 }.bind(this)); 77 }.bind(this));
97 78
98 document.addEventListener('prefsUpdate', this.handlePrefsUpdate_); 79 document.addEventListener(
80 'prefsUpdate', this.handlePrefsUpdate_.bind(this));
99 }, 81 },
100 82
101 /** 83 /**
102 * Set this.node_ to the previous interesting node. If no interesting node 84 * Set this.node_ to the next/previous interesting node. If no interesting
103 * comes before this.node_, set this.node_ to the last interesting node. 85 * node is found, set this.node_ to the first/last interesting node. If
86 * |doNext| is true, will search for next node. Otherwise, will search for
87 * previous node.
104 * 88 *
105 * @private 89 * @param {boolean} doNext
90 * @override
106 */ 91 */
107 moveToPrevious_: function() { 92 moveToNode: function(doNext) {
108 if (this.node_) { 93 let node = this.treeWalker_.moveToNode(this.node_, this.root_, doNext);
109 let prev = this.getPreviousNode_(this.node_); 94 if (node) {
110 while (prev && !this.isInteresting_(prev)) { 95 this.node_ = node;
111 prev = this.getPreviousNode_(prev); 96 this.printNode_(this.node_);
112 } 97 chrome.accessibilityPrivate.setFocusRing([this.node_.location]);
113 if (prev) {
114 this.node_ = prev;
115 this.printNode_(this.node_);
116 return;
117 }
118 } 98 }
119
120 if (this.root_) {
121 console.log('Reached the first interesting node. Restarting with last.')
122 let prev = this.getYoungestDescendant_(this.root_);
123 while (prev && !this.isInteresting_(prev)) {
124 prev = this.getPreviousNode_(prev);
125 }
126 if (prev) {
127 this.node_ = prev;
128 this.printNode_(this.node_);
129 return;
130 }
131 }
132
133 console.log('Found no interesting nodes to visit.')
134 }, 99 },
135 100
136 /** 101 /**
137 * Set this.node_ to the next interesting node. If no interesting node comes
138 * after this.node_, set this.node_ to the first interesting node.
139 *
140 * @private
141 */
142 moveToNext_: function() {
143 if (this.node_) {
144 let next = this.getNextNode_(this.node_);
145 while (next && !this.isInteresting_(next))
146 next = this.getNextNode_(next);
147 if (next) {
148 this.node_ = next;
149 this.printNode_(this.node_);
150 return;
151 }
152 }
153
154 if (this.root_) {
155 console.log('Reached the last interesting node. Restarting with first.');
156 let next = this.root_.firstChild;
157 while (next && !this.isInteresting_(next))
158 next = this.getNextNode_(next);
159 if (next) {
160 this.node_ = next;
161 this.printNode_(this.node_);
162 return;
163 }
164 }
165
166 console.log('Found no interesting nodes to visit.');
167 },
168
169 /**
170 * Given a flat list of nodes in pre-order, get the node that comes after
171 * |node|.
172 *
173 * @param {!AutomationNode} node
174 * @return {AutomationNode}
175 * @private
176 */
177 getNextNode_: function(node) {
178 // Check for child.
179 let child = node.firstChild;
180 if (child)
181 return child;
182
183 // No child. Check for right-sibling.
184 let sibling = node.nextSibling;
185 if (sibling)
186 return sibling;
187
188 // No right-sibling. Get right-sibling of closest ancestor.
189 let ancestor = node.parent;
190 while (ancestor) {
191 let aunt = ancestor.nextSibling;
192 if (aunt)
193 return aunt;
194 ancestor = ancestor.parent;
195 }
196
197 // No node found after |node|, so return null.
198 return null;
199 },
200
201 /**
202 * Given a flat list of nodes in pre-order, get the node that comes before
203 * |node|.
204 *
205 * @param {!AutomationNode} node
206 * @return {AutomationNode}
207 * @private
208 */
209 getPreviousNode_: function(node) {
210 // Check for left-sibling. Return its youngest descendant if it has one.
211 // Otherwise, return the sibling.
212 let sibling = node.previousSibling;
213 if (sibling) {
214 let descendant = this.getYoungestDescendant_(sibling);
215 if (descendant)
216 return descendant;
217 return sibling;
218 }
219
220 // No left-sibling. Check for parent.
221 let parent = node.parent;
222 if (parent)
223 return parent;
224
225 // No node found before |node|, so return null.
226 return null;
227 },
228
229 /**
230 * Get the youngest descendant of |node| if it has one.
231 *
232 * @param {!AutomationNode} node
233 * @return {AutomationNode}
234 * @private
235 */
236 getYoungestDescendant_: function(node) {
237 if (!node.lastChild)
238 return null;
239
240 while (node.lastChild)
241 node = node.lastChild;
242
243 return node;
244 },
245
246 /**
247 * Returns true if |node| is interesting.
248 *
249 * @param {!AutomationNode} node
250 * @return {boolean}
251 * @private
252 */
253 isInteresting_: function(node) {
254 return node.state && node.state.focusable;
255 },
256
257
258 /**
259 * Perform the default action on the currently selected node. 102 * Perform the default action on the currently selected node.
260 * 103 *
261 * @private 104 * @override
262 */ 105 */
263 doDefault_: function() { 106 doDefault: function() {
107 if (!this.node_)
108 return;
109
264 let state = this.node_.state; 110 let state = this.node_.state;
265 if (state && state.focusable) 111 if (state && state.focusable)
266 console.log('Node was focusable, doing default on it') 112 console.log('Node was focusable, doing default on it')
267 else if (state) 113 else if (state)
268 console.log('Node was not focusable, but still doing default'); 114 console.log('Node was not focusable, but still doing default');
269 else 115 else
270 console.log('Node has no state, still doing default'); 116 console.log('Node has no state, still doing default');
271 console.log('\n'); 117 console.log('\n');
272 this.node_.doDefault(); 118 this.node_.doDefault();
273 }, 119 },
274 120
275 /** 121 /**
276 * Open the options page in a new tab. 122 * Open the options page in a new tab.
277 * 123 *
278 * @private 124 * @override
279 */ 125 */
280 showOptionsPage_: function() { 126 showOptionsPage: function() {
281 let optionsPage = {url: 'options.html'}; 127 let optionsPage = {url: 'options.html'};
282 chrome.tabs.create(optionsPage); 128 chrome.tabs.create(optionsPage);
283 }, 129 },
284 130
285 /** 131 /**
132 * Perform actions as the result of actions by the user. Currently, restarts
133 * auto-scan if it is enabled.
134 *
135 * @override
136 */
137 performedUserAction: function() {
138 if (this.autoScanManager_.isRunning())
139 this.autoScanManager_.restart();
140 },
141
142 /**
286 * Handle a change in user preferences. 143 * Handle a change in user preferences.
287 * 144 *
288 * @param {!Event} event 145 * @param {!Event} event
289 * @private 146 * @private
290 */ 147 */
291 handlePrefsUpdate_: function(event) { 148 handlePrefsUpdate_: function(event) {
292 let updatedPrefs = event.detail; 149 let updatedPrefs = event.detail;
293 for (let key of Object.keys(updatedPrefs)) { 150 for (let key of Object.keys(updatedPrefs)) {
294 switch (key) { 151 switch (key) {
295 case 'enableAutoScan': 152 case 'enableAutoScan':
296 console.log('Auto-scan enabled set to: ' + updatedPrefs[key]); 153 this.autoScanManager_.setEnabled(updatedPrefs[key]);
297 break; 154 break;
298 case 'autoScanTime': 155 case 'autoScanTime':
299 console.log( 156 this.autoScanManager_.setScanTime(updatedPrefs[key]);
300 'Auto-scan time set to: ' + updatedPrefs[key] + " seconds");
301 break; 157 break;
302 } 158 }
303 } 159 }
304 }, 160 },
305 161
306 // TODO(elichtenberg): Move print functions to a custom logger class. Only 162 // TODO(elichtenberg): Move print functions to a custom logger class. Only
307 // log when debuggingEnabled is true. 163 // log when debuggingEnabled is true.
308 /** 164 /**
309 * Print out details about a node. 165 * Print out details about a node.
310 * 166 *
311 * @param {AutomationNode} node 167 * @param {chrome.automation.AutomationNode} node
312 * @private 168 * @private
313 */ 169 */
314 printNode_: function(node) { 170 printNode_: function(node) {
315 if (node) { 171 if (node) {
316 console.log('Name = ' + node.name); 172 console.log('Name = ' + node.name);
317 console.log('Role = ' + node.role); 173 console.log('Role = ' + node.role);
318 if (!node.parent) 174 if (!node.parent)
319 console.log('At index ' + node.indexInParent + ', has no parent'); 175 console.log('At index ' + node.indexInParent + ', has no parent');
320 else { 176 else {
321 let numSiblings = node.parent.children.length; 177 let numSiblings = node.parent.children.length;
322 console.log( 178 console.log(
323 'At index ' + node.indexInParent + ', there are ' 179 'At index ' + node.indexInParent + ', there are '
324 + numSiblings + ' siblings'); 180 + numSiblings + ' siblings');
325 } 181 }
326 console.log('Has ' + node.children.length + ' children'); 182 console.log('Has ' + node.children.length + ' children');
327 } else { 183 } else {
328 console.log('Node is null'); 184 console.log('Node is null');
329 } 185 }
330 console.log(node); 186 console.log(node);
331 console.log('\n'); 187 console.log('\n');
332 }, 188 },
333 189
334 /** 190 /**
191 * Move to the next sibling of this.node_ if it has one.
192 *
193 * @override
194 */
195 debugMoveToNext: function() {
196 let next = this.treeWalker_.debugMoveToNext(this.node_);
197 if (next) {
198 this.node_ = next;
199 this.printNode_(this.node_);
200 chrome.accessibilityPrivate.setFocusRing([this.node_.location]);
201 }
202 },
203
204 /**
335 * Move to the previous sibling of this.node_ if it has one. 205 * Move to the previous sibling of this.node_ if it has one.
336 * 206 *
337 * @private 207 * @override
338 */ 208 */
339 debugMoveToPrevious_: function() { 209 debugMoveToPrevious: function() {
340 let previous = this.node_.previousSibling; 210 let prev = this.treeWalker_.debugMoveToPrevious(this.node_);
341 if (previous) { 211 if (prev) {
342 this.node_ = previous; 212 this.node_ = prev;
343 this.printNode_(this.node_); 213 this.printNode_(this.node_);
344 } else { 214 chrome.accessibilityPrivate.setFocusRing([this.node_.location]);
345 console.log('Node is first of siblings');
346 console.log('\n');
347 }
348 },
349
350 /**
351 * Move to the next sibling of this.node_ if it has one.
352 *
353 * @private
354 */
355 debugMoveToNext_: function() {
356 let next = this.node_.nextSibling;
357 if (next) {
358 this.node_ = next;
359 this.printNode_(this.node_);
360 } else {
361 console.log('Node is last of siblings');
362 console.log('\n');
363 } 215 }
364 }, 216 },
365 217
366 /** 218 /**
367 * Move to the first child of this.node_ if it has one. 219 * Move to the first child of this.node_ if it has one.
368 * 220 *
369 * @private 221 * @override
370 */ 222 */
371 debugMoveToFirstChild_: function() { 223 debugMoveToFirstChild: function() {
372 let child = this.node_.firstChild; 224 let child = this.treeWalker_.debugMoveToFirstChild(this.node_);
373 if (child) { 225 if (child) {
374 this.node_ = child; 226 this.node_ = child;
375 this.printNode_(this.node_); 227 this.printNode_(this.node_);
376 } else { 228 chrome.accessibilityPrivate.setFocusRing([this.node_.location]);
377 console.log('Node has no children');
378 console.log('\n');
379 } 229 }
380 }, 230 },
381 231
382 /** 232 /**
383 * Move to the parent of this.node_ if it has one. 233 * Move to the parent of this.node_ if it has one.
384 * 234 *
385 * @private 235 * @override
386 */ 236 */
387 debugMoveToParent_: function() { 237 debugMoveToParent: function() {
388 let parent = this.node_.parent; 238 let parent = this.treeWalker_.debugMoveToParent(this.node_);
389 if (parent) { 239 if (parent) {
390 this.node_ = parent; 240 this.node_ = parent;
391 this.printNode_(this.node_); 241 this.printNode_(this.node_);
392 } else { 242 chrome.accessibilityPrivate.setFocusRing([this.node_.location]);
393 console.log('Node has no parent');
394 console.log('\n');
395 } 243 }
396 } 244 }
397 }; 245 };
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698