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

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

Issue 2738893003: Use keys 1 through 3 to switch between and click on focusable elements. (Closed)
Patch Set: Refactored tree walking code. Created 3 years, 9 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
« no previous file with comments | « chrome/browser/chromeos/accessibility/switch_access_event_handler.cc ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 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();
OLDNEW
« no previous file with comments | « chrome/browser/chromeos/accessibility/switch_access_event_handler.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698