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

Side by Side Diff: chrome/browser/resources/access_chromevox/chromevox/injected/navigation_manager.js

Issue 6254007: Adding ChromeVox as a component extensions (enabled only for ChromeOS, for no... (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: '' Created 9 years, 11 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 | Annotate | Revision Log
Property Changes:
Added: svn:executable
+ *
Added: svn:eol-style
+ LF
OLDNEW
(Empty)
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 /**
6 * @fileoverview Manages navigation within a page.
7 * This unifies navigation by the DOM walker and by WebKit selection.
8 */
9
10 goog.provide('cvox.ChromeVoxNavigationManager');
11
12 goog.require('cvox.ChromeVoxChoiceWidget');
13 goog.require('cvox.DomUtil');
14 goog.require('cvox.LinearDomWalker');
15 goog.require('cvox.SelectionUtil');
16 goog.require('cvox.SelectionWalker');
17
18 /**
19 * @constructor
20 */
21 cvox.ChromeVoxNavigationManager = function() {
22 this.currentNode = null;
23 this.nodeInformationArray = new Array();
24 this.currentNavStrategy = 2;
25 this.lastUsedNavStrategy = 2;
26 this.linearDomWalker = new cvox.LinearDomWalker();
27 this.selectionWalker = new cvox.SelectionWalker();
28 this.customWalker = null;
29 this.selectionUniqueAncestors = [];
30 this.choiceWidget = new cvox.ChromeVoxChoiceWidget();
31 };
32
33 /**
34 * @type {Object.<string, number>}
35 */
36 cvox.ChromeVoxNavigationManager.STRATEGIES =
37 {'SELECTION' : 0, 'LINEARDOM' : 1, 'SMART' : 2, 'CUSTOM' : 3};
38
39 /**
40 * @type {Array.<string>}
41 */
42 cvox.ChromeVoxNavigationManager.STRATEGY_NAMES =
43 ['SELECTION', 'OBJECT', 'GROUP', 'CUSTOM'];
44
45 /**
46 * Moves forward using the current navigation strategy.
47 */
48 cvox.ChromeVoxNavigationManager.prototype.next = function() {
49 switch (this.currentNavStrategy) {
50 default:
51 case cvox.ChromeVoxNavigationManager.STRATEGIES.CUSTOM:
52 this.customWalker.next();
53 break;
54 case cvox.ChromeVoxNavigationManager.STRATEGIES.SMART:
55 case cvox.ChromeVoxNavigationManager.STRATEGIES.LINEARDOM:
56 var node = this.linearDomWalker.next();
57 if (node) {
58 cvox.SelectionUtil.selectAllTextInNode(node);
59 this.currentNode = node;
60 }
61 break;
62
63 case cvox.ChromeVoxNavigationManager.STRATEGIES.SELECTION:
64 this.selectionUniqueAncestors = [];
65 var movedOk = this.selectionWalker.next();
66 if (!movedOk) {
67 var selectionNode = this.linearDomWalker.next();
68 this.selectionUniqueAncestors =
69 this.linearDomWalker.getUniqueAncestors();
70 if (selectionNode) {
71 this.currentNode = selectionNode;
72 this.selectionWalker.setCurrentNode(this.currentNode);
73 this.selectionWalker.next();
74 }
75 }
76 break;
77 }
78 };
79
80 /**
81 * Moves backward using the current navigation strategy.
82 */
83 cvox.ChromeVoxNavigationManager.prototype.previous = function() {
84 switch (this.currentNavStrategy) {
85 default:
86 case cvox.ChromeVoxNavigationManager.STRATEGIES.CUSTOM:
87 this.customWalker.previous();
88 break;
89 case cvox.ChromeVoxNavigationManager.STRATEGIES.SMART:
90 case cvox.ChromeVoxNavigationManager.STRATEGIES.LINEARDOM:
91 var node = this.linearDomWalker.previous();
92 if (node) {
93 cvox.SelectionUtil.selectAllTextInNode(node);
94 this.currentNode = node;
95 }
96 break;
97
98 case cvox.ChromeVoxNavigationManager.STRATEGIES.SELECTION:
99 this.selectionUniqueAncestors = [];
100 var movedOk = this.selectionWalker.previous();
101 if (!movedOk) {
102 var selectionNode = this.linearDomWalker.previous();
103 this.selectionUniqueAncestors =
104 this.linearDomWalker.getUniqueAncestors();
105 if (selectionNode) {
106 this.currentNode = selectionNode;
107 this.selectionWalker.setCurrentNode(this.currentNode);
108 cvox.SelectionUtil.selectAllTextInNode(this.currentNode);
109 window.getSelection().collapseToEnd();
110 this.selectionWalker.previous();
111 }
112 }
113 break;
114 }
115 };
116
117 /**
118 * Moves up a level of granularity.
119 */
120 cvox.ChromeVoxNavigationManager.prototype.up = function() {
121 switch (this.currentNavStrategy) {
122 default:
123 case cvox.ChromeVoxNavigationManager.STRATEGIES.CUSTOM:
124 break;
125 case cvox.ChromeVoxNavigationManager.STRATEGIES.SMART:
126 if (!!this.customWalker) {
127 this.lastUsedNavStrategy = this.currentNavStrategy;
128 this.currentNavStrategy = 3;
129 this.customWalker.setCurrentNode(this.currentNode);
130 this.customWalker.goToCurrentItem();
131 }
132 break;
133
134 case cvox.ChromeVoxNavigationManager.STRATEGIES.LINEARDOM:
135 this.lastUsedNavStrategy = this.currentNavStrategy;
136 this.currentNavStrategy = 2;
137 this.linearDomWalker.setSmartNavEnabled(true);
138 var node = this.currentNode;
139 while (this.linearDomWalker.isLeafNode(node)) {
140 this.currentNode = node;
141 node = node.parentNode;
142 }
143 this.linearDomWalker.setCurrentNode(this.currentNode);
144 if (this.currentNode !== null) {
145 this.previous();
146 }
147 this.next();
148 break;
149
150 case cvox.ChromeVoxNavigationManager.STRATEGIES.SELECTION:
151 var changed = this.selectionWalker.lessGranular();
152 if (!changed) {
153 this.lastUsedNavStrategy = this.currentNavStrategy;
154 this.currentNavStrategy = 1;
155 cvox.SelectionUtil.selectAllTextInNode(this.currentNode);
156 } else {
157 this.selectionWalker.previous();
158 this.selectionWalker.next();
159 }
160 break;
161 }
162 };
163
164
165 /**
166 * Moves down a level of granularity.
167 */
168 cvox.ChromeVoxNavigationManager.prototype.down = function() {
169 switch (this.currentNavStrategy) {
170 default:
171 case cvox.ChromeVoxNavigationManager.STRATEGIES.CUSTOM:
172 this.lastUsedNavStrategy = this.currentNavStrategy;
173 this.currentNavStrategy = 2;
174 this.linearDomWalker.setSmartNavEnabled(true);
175 if (this.customWalker.getCurrentNode() != null) {
176 this.currentNode = this.customWalker.getCurrentNode();
177 this.linearDomWalker.setCurrentNode(this.currentNode);
178 }
179 break;
180 case cvox.ChromeVoxNavigationManager.STRATEGIES.SMART:
181 this.lastUsedNavStrategy = this.currentNavStrategy;
182 this.currentNavStrategy = 1;
183 this.linearDomWalker.setSmartNavEnabled(false);
184 if (this.currentNode !== null) {
185 this.previous();
186 }
187 this.next();
188 break;
189
190 case cvox.ChromeVoxNavigationManager.STRATEGIES.LINEARDOM:
191 this.lastUsedNavStrategy = this.currentNavStrategy;
192 this.currentNavStrategy = 0;
193 this.selectionWalker.setCurrentNode(this.currentNode);
194 this.selectionWalker.next();
195 break;
196
197 case cvox.ChromeVoxNavigationManager.STRATEGIES.SELECTION:
198 var changed = this.selectionWalker.moreGranular();
199 if (changed) {
200 this.selectionWalker.previous();
201 this.selectionWalker.next();
202 }
203 break;
204 }
205 };
206
207 /**
208 * Moves to the next occurrence of a node that matches the given predicate,
209 * if one exists, using the linearDomWalker.
210 * @param {function(Array.<Node>)} predicate A function taking an array
211 * of unique ancestor nodes as a parameter and returning true if it's
212 * what to search for.
213 * @return {boolean} True if a match was found.
214 */
215 cvox.ChromeVoxNavigationManager.prototype.findNext = function(predicate) {
216 this.syncPosition();
217 var node = undefined;
218 while (true) {
219 node = this.linearDomWalker.next();
220 if (!node) {
221 break;
222 }
223
224 if (predicate(this.linearDomWalker.getUniqueAncestors())) {
225 break;
226 }
227 }
228
229 if (node) {
230 cvox.SelectionUtil.selectAllTextInNode(node);
231 this.currentNode = node;
232 return true;
233 }
234
235 return false;
236 };
237
238 /**
239 * Moves to the previous occurrence of a node that matches the given predicate,
240 * if one exists, using the linearDomWalker.
241 * @param {function(Array.<Node>)} predicate A function taking an array
242 * of unique ancestor nodes as a parameter and returning true if it's
243 * what to search for.
244 * @return {boolean} True if a match was found.
245 */
246 cvox.ChromeVoxNavigationManager.prototype.findPrevious = function(predicate) {
247 this.syncPosition();
248 var node = undefined;
249 while (true) {
250 node = this.linearDomWalker.previous();
251 if (!node) {
252 break;
253 }
254
255 if (predicate(this.linearDomWalker.getUniqueAncestors())) {
256 break;
257 }
258 }
259
260 if (node) {
261 cvox.SelectionUtil.selectAllTextInNode(node);
262 this.currentNode = node;
263 return true;
264 }
265
266 return false;
267 };
268
269 /**
270 * Returns the current navigation strategy.
271 *
272 * @return {string} The strategy that is being used.
273 */
274 cvox.ChromeVoxNavigationManager.prototype.getStrategy = function() {
275 return cvox.ChromeVoxNavigationManager.STRATEGY_NAMES[
276 this.currentNavStrategy];
277 };
278
279 /**
280 * Returns the current selection granularity.
281 *
282 * @return {string} The selection granularity that is being used.
283 */
284 cvox.ChromeVoxNavigationManager.prototype.getGranularity = function() {
285 return this.selectionWalker.getGranularity();
286 };
287
288 /**
289 * Synchronizes the current position between the different navigation
290 * strategies.
291 */
292 cvox.ChromeVoxNavigationManager.prototype.syncPosition = function() {
293 if (!this.currentNode) {
294 this.currentNode = document.body;
295 }
296 if (this.currentNavStrategy != this.lastUsedNavStrategy) {
297 if ((this.lastUsedNavStrategy ==
298 cvox.ChromeVoxNavigationManager.STRATEGIES.LINEARDOM) ||
299 (this.lastUsedNavStrategy ==
300 cvox.ChromeVoxNavigationManager.STRATEGIES.SMART)) {
301 this.syncToNode(this.currentNode);
302 } else {
303 this.syncToSelection();
304 }
305 }
306 this.lastUsedNavStrategy = this.currentNavStrategy;
307 };
308
309 /**
310 * Synchronizes the navigation strategies to the current selection.
311 */
312 cvox.ChromeVoxNavigationManager.prototype.syncToSelection = function() {
313 if (window.getSelection() && window.getSelection().anchorNode) {
314 this.currentNode = window.getSelection().anchorNode;
315 this.linearDomWalker.setCurrentNode(this.currentNode);
316 }
317 };
318
319 /**
320 * Synchronizes the navigation strategies to the targetNode.
321 *
322 * @param {Node} targetNode The node that the navigation strategies should be
323 * synced to.
324 */
325 cvox.ChromeVoxNavigationManager.prototype.syncToNode = function(targetNode) {
326 if (cvox.DomUtil.isDescendantOfNode(this.currentNode, targetNode)) {
327 // User is already synced at a more specific level than the target;
328 // therefore ignore the sync request.
329 return;
330 }
331 this.currentNode = targetNode;
332 this.linearDomWalker.setCurrentNode(targetNode);
333 var range = document.createRange();
334 range.selectNode(this.currentNode);
335 window.getSelection().removeAllRanges();
336 window.getSelection().addRange(range);
337 window.getSelection().collapseToStart();
338 };
339
340 /**
341 * Returns only the text content for the current position.
342 *
343 * @return {string} The current text content.
344 */
345 cvox.ChromeVoxNavigationManager.prototype.getCurrentContent = function() {
346 switch (this.currentNavStrategy) {
347 default:
348 case cvox.ChromeVoxNavigationManager.STRATEGIES.CUSTOM:
349 return this.customWalker.getCurrentContent();
350 break;
351 case cvox.ChromeVoxNavigationManager.STRATEGIES.SMART:
352 case cvox.ChromeVoxNavigationManager.STRATEGIES.LINEARDOM:
353 return cvox.DomUtil.getText(this.currentNode);
354 break;
355
356 case cvox.ChromeVoxNavigationManager.STRATEGIES.SELECTION:
357 return this.selectionWalker.getCurrentContent();
358 break;
359 }
360 };
361
362 /**
363 * Returns a complete description of the current position, including
364 * the text content and annotations such as "link", "button", etc.
365 *
366 * @return {string} The summary of the current position.
367 */
368 cvox.ChromeVoxNavigationManager.prototype.getCurrentDescription = function() {
369 switch (this.currentNavStrategy) {
370 default:
371 case cvox.ChromeVoxNavigationManager.STRATEGIES.CUSTOM:
372 return this.customWalker.getCurrentDescription();
373 break;
374 case cvox.ChromeVoxNavigationManager.STRATEGIES.SMART:
375 // Use a linear DOM walker in non-smart mode to traverse all of the
376 // nodes inside the current smart node and append all of their
377 // descriptions.
378 var description = '';
379 var walker = new cvox.LinearDomWalker();
380 walker.currentNode = this.linearDomWalker.currentNode;
381 walker.useSmartNav = false;
382 walker.previous();
383 walker.next();
384 while (cvox.DomUtil.isDescendantOfNode(
385 walker.currentNode, this.linearDomWalker.currentNode)) {
386 description = description + ' ' +
387 cvox.DomUtil.getText(walker.currentNode) + ' ' +
388 cvox.DomUtil.getInformationFromAncestors(
389 walker.getUniqueAncestors());
390 walker.next();
391 }
392 return description;
393
394 case cvox.ChromeVoxNavigationManager.STRATEGIES.LINEARDOM:
395 return this.getCurrentContent() + ' ' +
396 cvox.DomUtil.getInformationFromAncestors(this.getChangedAncestors());
397
398 case cvox.ChromeVoxNavigationManager.STRATEGIES.SELECTION:
399 return this.getCurrentContent() + ' ' +
400 cvox.DomUtil.getInformationFromAncestors(
401 this.selectionUniqueAncestors);
402 }
403 };
404
405 /**
406 * Returns an array of ancestor nodes that have been changed between the
407 * previous position and the current current position.
408 *
409 * @return {Array.<Node>} The current content.
410 */
411 cvox.ChromeVoxNavigationManager.prototype.getChangedAncestors = function() {
412 return this.linearDomWalker.getUniqueAncestors();
413 };
414
415 /**
416 * Sets the browser's focus to the current node.
417 */
418 cvox.ChromeVoxNavigationManager.prototype.setFocus = function() {
419 cvox.DomUtil.setFocus(this.linearDomWalker.getCurrentNode());
420 };
421
422 /**
423 * Acts on the current item and displays a disambiguation dialog
424 * if more than one action is possible.
425 *
426 * @return {boolean} True if an action was taken.
427 */
428 cvox.ChromeVoxNavigationManager.prototype.actOnCurrentItem = function() {
429 if (this.currentNavStrategy ==
430 cvox.ChromeVoxNavigationManager.STRATEGIES.CUSTOM) {
431 return this.customWalker.actOnCurrentItem();
432 } else if (this.currentNavStrategy ==
433 cvox.ChromeVoxNavigationManager.STRATEGIES.SMART) {
434 if (this.currentNode && this.currentNode.tagName &&
435 (this.currentNode.tagName == 'A')) {
436 cvox.DomUtil.clickElem(this.currentNode, false);
437 return true;
438 } else {
439 var aNodes = this.currentNode.getElementsByTagName('A');
440 if (aNodes.length == 1) {
441 cvox.DomUtil.clickElem(aNodes[0], false);
442 return true;
443 } else if (aNodes.length > 1) {
444 var descriptions = new Array();
445 var functions = new Array();
446 for (var i = 0, link; link = aNodes[i]; i++) {
447 if (cvox.DomUtil.hasContent(link)) {
448 descriptions.push(cvox.DomUtil.getText(link));
449 functions.push(cvox.ChromeVoxNavigationManager
450 .createSimpleClickFunction(link));
451 }
452 }
453 this.choiceWidget.show(descriptions, functions,
454 descriptions.toString());
455 return true;
456 }
457 }
458 }
459 return false;
460 };
461
462 /**
463 * Checks if the navigation manager is able to act on the current item.
464 *
465 * @return {boolean} True if some action is possible.
466 */
467 cvox.ChromeVoxNavigationManager.prototype.canActOnCurrentItem = function() {
468 if (this.currentNavStrategy ==
469 cvox.ChromeVoxNavigationManager.STRATEGIES.CUSTOM) {
470 return this.customWalker.canActOnCurrentItem();
471 }
472 if (this.currentNavStrategy ==
473 cvox.ChromeVoxNavigationManager.STRATEGIES.SMART) {
474 if (this.currentNode && this.currentNode.tagName &&
475 (this.currentNode.tagName == 'A')) {
476 return true;
477 } else {
478 var aNodes = this.currentNode.getElementsByTagName('A');
479 if (aNodes.length > 0) {
480 return true;
481 }
482 }
483 }
484 // Anything that is DOM level or lower will always be handled by the browser.
485 return false;
486 };
487
488 /**
489 * Creates a simple function that will click on the given targetNode when
490 * invoked.
491 * Note that we are using this function because functions created inside a loop
492 * have to be created by another function and not within the loop directly.
493 *
494 * See: http://joust.kano.net/weblog/archive/2005/08/08/
495 * a-huge-gotcha-with-javascript-closures/
496 * @param {Node} targetNode The target node to click on.
497 * @return {function()} A function that will click on the given targetNode.
498 */
499 cvox.ChromeVoxNavigationManager.createSimpleClickFunction = function(
500 targetNode) {
501 var target = targetNode.cloneNode(true);
502 return function() { cvox.DomUtil.clickElem(target, false); };
503 };
504
505 /**
506 * Sets the custom walker to use for the current site.
507 *
508 * @param {Object} customWalkerObj The custom walker to use.
509 */
510 cvox.ChromeVoxNavigationManager.prototype.setCustomWalker =
511 function(customWalkerObj) {
512 this.customWalker = customWalkerObj;
513 this.currentNavStrategy = 3;
514 this.lastUsedNavStrategy = 3;
515 };
516
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698