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

Side by Side Diff: chrome/browser/resources/chromeos/chromevox/cvox2/background/background.js

Issue 637223006: Initial support for Ranges over automation nodes (used to track ChromeVox focus). (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Indent; TODO. Created 6 years, 2 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 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 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 /** 5 /**
6 * @fileoverview The entry point for all ChromeVox2 related code for the 6 * @fileoverview The entry point for all ChromeVox2 related code for the
7 * background page. 7 * background page.
8 */ 8 */
9 9
10 goog.provide('Background'); 10 goog.provide('Background');
11 goog.provide('global'); 11 goog.provide('global');
12 12
13 goog.require('AutomationPredicate'); 13 goog.require('AutomationPredicate');
14 goog.require('AutomationUtil'); 14 goog.require('AutomationUtil');
15 goog.require('cursors.Cursor'); 15 goog.require('cursors.Cursor');
16 goog.require('cvox.TabsApiHandler'); 16 goog.require('cvox.TabsApiHandler');
17 17
18 goog.scope(function() { 18 goog.scope(function() {
19 var AutomationNode = chrome.automation.AutomationNode;
19 var Dir = AutomationUtil.Dir; 20 var Dir = AutomationUtil.Dir;
20 var EventType = chrome.automation.EventType; 21 var EventType = chrome.automation.EventType;
21 var AutomationNode = chrome.automation.AutomationNode;
22 22
23 /** Classic Chrome accessibility API. */ 23 /** Classic Chrome accessibility API. */
24 global.accessibility = 24 global.accessibility =
25 chrome.accessibilityPrivate || chrome.experimental.accessibility; 25 chrome.accessibilityPrivate || chrome.experimental.accessibility;
26 26
27 /** 27 /**
28 * ChromeVox2 background page. 28 * ChromeVox2 background page.
29 * @constructor 29 * @constructor
30 */ 30 */
31 Background = function() { 31 Background = function() {
32 /** 32 /**
33 * A list of site substring patterns to use with ChromeVox next. Keep these 33 * A list of site substring patterns to use with ChromeVox next. Keep these
34 * strings relatively specific. 34 * strings relatively specific.
35 * @type {!Array.<string>} 35 * @type {!Array.<string>}
36 * @private 36 * @private
37 */ 37 */
38 this.whitelist_ = ['http://www.chromevox.com/', 'chromevox_next_test']; 38 this.whitelist_ = ['http://www.chromevox.com/', 'chromevox_next_test'];
39 39
40 /** 40 /**
41 * @type {cvox.TabsApiHandler} 41 * @type {cvox.TabsApiHandler}
42 * @private 42 * @private
43 */ 43 */
44 this.tabsHandler_ = new cvox.TabsApiHandler(cvox.ChromeVox.tts, 44 this.tabsHandler_ = new cvox.TabsApiHandler(cvox.ChromeVox.tts,
45 cvox.ChromeVox.braille, 45 cvox.ChromeVox.braille,
46 cvox.ChromeVox.earcons); 46 cvox.ChromeVox.earcons);
47 47
48 /** 48 /**
49 * @type {chrome.automation.AutomationNode} 49 * @type {cursors.Range}
50 * @private 50 * @private
51 */ 51 */
52 this.currentNode_ = null; 52 this.currentRange_ = null;
53 53
54 /** 54 /**
55 * Whether ChromeVox Next is active. 55 * Whether ChromeVox Next is active.
56 * @type {boolean} 56 * @type {boolean}
57 * @private 57 * @private
58 */ 58 */
59 this.active_ = false; 59 this.active_ = false;
60 60
61 // Only needed with unmerged ChromeVox classic loaded before. 61 // Only needed with unmerged ChromeVox classic loaded before.
62 global.accessibility.setAccessibilityEnabled(false); 62 global.accessibility.setAccessibilityEnabled(false);
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after
122 /** 122 /**
123 * Handles chrome.commands.onCommand. 123 * Handles chrome.commands.onCommand.
124 * @param {string} command 124 * @param {string} command
125 */ 125 */
126 onGotCommand: function(command) { 126 onGotCommand: function(command) {
127 if (command == 'toggleChromeVoxVersion') { 127 if (command == 'toggleChromeVoxVersion') {
128 this.toggleChromeVoxVersion(); 128 this.toggleChromeVoxVersion();
129 return; 129 return;
130 } 130 }
131 131
132 if (!this.active_ || !this.current_) 132 if (!this.active_ || !this.currentRange_)
133 return; 133 return;
134 134
135 var previous = this.current_; 135 var current = this.currentRange_;
136 var current = this.current_;
137
138 var dir = Dir.FORWARD; 136 var dir = Dir.FORWARD;
139 var pred = null; 137 var pred = null;
140 switch (command) { 138 switch (command) {
141 case 'nextHeading': 139 case 'nextHeading':
142 dir = Dir.FORWARD; 140 dir = Dir.FORWARD;
143 pred = AutomationPredicate.heading; 141 pred = AutomationPredicate.heading;
144 break; 142 break;
145 case 'previousHeading': 143 case 'previousHeading':
146 dir = Dir.BACKWARD; 144 dir = Dir.BACKWARD;
147 pred = AutomationPredicate.heading; 145 pred = AutomationPredicate.heading;
148 break; 146 break;
149 case 'nextLine': 147 case 'nextLine':
150 dir = Dir.FORWARD; 148 current = current.move(cursors.Unit.LINE, Dir.FORWARD);
151 pred = AutomationPredicate.inlineTextBox;
152 break; 149 break;
153 case 'previousLine': 150 case 'previousLine':
154 dir = Dir.BACKWARD; 151 current = current.move(cursors.Unit.LINE, Dir.BACKWARD);
155 pred = AutomationPredicate.inlineTextBox;
156 break; 152 break;
157 case 'nextLink': 153 case 'nextLink':
158 dir = Dir.FORWARD; 154 dir = Dir.FORWARD;
159 pred = AutomationPredicate.link; 155 pred = AutomationPredicate.link;
160 break; 156 break;
161 case 'previousLink': 157 case 'previousLink':
162 dir = Dir.BACKWARD; 158 dir = Dir.BACKWARD;
163 pred = AutomationPredicate.link; 159 pred = AutomationPredicate.link;
164 break; 160 break;
165 case 'nextElement': 161 case 'nextElement':
166 current = current.role == chrome.automation.RoleType.inlineTextBox ? 162 current = current.move(cursors.Unit.NODE, Dir.FORWARD);
167 current.parent() : current;
168 current = AutomationUtil.findNextNode(current,
169 Dir.FORWARD,
170 AutomationPredicate.inlineTextBox);
171 current = current ? current.parent() : current;
172 break; 163 break;
173 case 'previousElement': 164 case 'previousElement':
174 current = current.role == chrome.automation.RoleType.inlineTextBox ? 165 current = current.move(cursors.Unit.NODE, Dir.BACKWARD);
175 current.parent() : current;
176 current = AutomationUtil.findNextNode(current,
177 Dir.BACKWARD,
178 AutomationPredicate.inlineTextBox);
179 current = current ? current.parent() : current;
180 break; 166 break;
181 case 'goToBeginning': 167 case 'goToBeginning':
182 current = AutomationUtil.findNodePost(current.root, 168 var node = AutomationUtil.findNodePost(current.getStart().getNode().root,
183 Dir.FORWARD, 169 Dir.FORWARD,
184 AutomationPredicate.inlineTextBox); 170 AutomationPredicate.leaf);
171 if (node)
172 current = cursors.Range.fromNode(node);
185 break; 173 break;
186 case 'goToEnd': 174 case 'goToEnd':
187 current = AutomationUtil.findNodePost(current.root, 175 var node =
176 AutomationUtil.findNodePost(current.getStart().getNode().root,
188 Dir.BACKWARD, 177 Dir.BACKWARD,
189 AutomationPredicate.inlineTextBox); 178 AutomationPredicate.leaf);
179 if (node)
180 current = cursors.Range.fromNode(node);
190 break; 181 break;
191 } 182 }
192 183
193 if (pred) 184 if (pred) {
194 current = AutomationUtil.findNextNode(current, dir, pred); 185 var node = AutomationUtil.findNextNode(
186 current.getBound(dir).getNode(), dir, pred);
187
188 if (node)
189 current = cursors.Range.fromNode(node);
190 }
195 191
196 if (current) { 192 if (current) {
197 current.focus(); 193 // TODO(dtseng): Figure out what it means to focus a range.
194 current.getStart().getNode().focus();
198 195
199 this.onFocus({target: current}); 196 this.currentRange_ = current;
197 this.handleOutput(this.currentRange_);
200 } 198 }
201 }, 199 },
202 200
203 /** 201 /**
204 * Provides all feedback once ChromeVox's focus changes. 202 * Provides all feedback once ChromeVox's focus changes.
205 * @param {Object} evt 203 * @param {Object} evt
206 */ 204 */
207 onFocus: function(evt) { 205 onFocus: function(evt) {
208 var node = evt.target; 206 var node = evt.target;
209 if (!node) 207 if (!node)
210 return; 208 return;
211 209
212 this.current_ = node; 210 this.currentRange_ = cursors.Range.fromNode(node);
213 var container = node; 211 this.handleOutput(this.currentRange_);
214 while (container &&
215 (container.role == chrome.automation.RoleType.inlineTextBox ||
216 container.role == chrome.automation.RoleType.staticText))
217 container = container.parent();
218
219 var role = container ? container.role : node.role;
220
221 var output =
222 [node.attributes.name, node.attributes.value, role].join(', ');
223 cvox.ChromeVox.tts.speak(output, cvox.QueueMode.FLUSH);
224 cvox.ChromeVox.braille.write(cvox.NavBraille.fromText(output));
225 chrome.accessibilityPrivate.setFocusRing([evt.target.location]);
226 }, 212 },
227 213
228 /** 214 /**
229 * Provides all feedback once a load complete event fires. 215 * Provides all feedback once a load complete event fires.
230 * @param {Object} evt 216 * @param {Object} evt
231 */ 217 */
232 onLoadComplete: function(evt) { 218 onLoadComplete: function(evt) {
233 if (this.current_) 219 if (this.currentRange_)
234 return; 220 return;
235 221
236 this.current_ = AutomationUtil.findNodePost(evt.target, 222 var node = AutomationUtil.findNodePost(evt.target,
237 Dir.FORWARD, 223 Dir.FORWARD,
238 AutomationPredicate.inlineTextBox); 224 AutomationPredicate.leaf);
239 this.onFocus({target: this.current_}); 225 if (node)
226 this.currentRange_ = cursors.Range.fromNode(node);
227
228 if (this.currentRange_)
229 this.handleOutput(this.currentRange_);
240 }, 230 },
241 231
242 /** 232 /**
243 * @private 233 * @private
244 * @param {string} url 234 * @param {string} url
245 * @return {boolean} Whether the given |url| is whitelisted. 235 * @return {boolean} Whether the given |url| is whitelisted.
246 */ 236 */
247 isWhitelisted_: function(url) { 237 isWhitelisted_: function(url) {
248 return this.whitelist_.some(function(item) { 238 return this.whitelist_.some(function(item) {
249 return url.indexOf(item) != -1; 239 return url.indexOf(item) != -1;
(...skipping 21 matching lines...) Expand all
271 opt_options.next = !this.active_; 261 opt_options.next = !this.active_;
272 opt_options.classic = !opt_options.next; 262 opt_options.classic = !opt_options.next;
273 } 263 }
274 264
275 if (opt_options.next) { 265 if (opt_options.next) {
276 chrome.automation.getTree(this.onGotTree); 266 chrome.automation.getTree(this.onGotTree);
277 this.active_ = true; 267 this.active_ = true;
278 } else { 268 } else {
279 if (this.active_) { 269 if (this.active_) {
280 for (var eventType in this.listeners_) { 270 for (var eventType in this.listeners_) {
281 this.current_.root.removeEventListener( 271 this.currentRange_.getStart().getNode().root.removeEventListener(
282 eventType, this.listeners_[eventType], true); 272 eventType, this.listeners_[eventType], true);
283 } 273 }
284 } 274 }
285 this.active_ = false; 275 this.active_ = false;
286 } 276 }
287 277
288 chrome.tabs.query({active: true}, function(tabs) { 278 chrome.tabs.query({active: true}, function(tabs) {
289 if (opt_options.classic) { 279 if (opt_options.classic) {
290 cvox.ChromeVox.injectChromeVoxIntoTabs(tabs); 280 cvox.ChromeVox.injectChromeVoxIntoTabs(tabs);
291 } else { 281 } else {
292 tabs.forEach(function(tab) { 282 tabs.forEach(function(tab) {
293 this.disableClassicChromeVox_(tab.id); 283 this.disableClassicChromeVox_(tab.id);
294 }.bind(this)); 284 }.bind(this));
295 } 285 }
296 }.bind(this)); 286 }.bind(this));
287 },
288
289 /**
290 * Handles output of a Range.
291 * @param {!cursors.Range} range Current location.
292 */
293 handleOutput: function(range) {
294 // TODO(dtseng): This is just placeholder logic for generating descriptions
295 // pending further design discussion.
296 function getCursorDesc(cursor) {
297 var node = cursor.getNode();
298 var container = node;
299 while (container &&
300 (container.role == chrome.automation.RoleType.inlineTextBox ||
301 container.role == chrome.automation.RoleType.staticText))
302 container = container.parent();
303
304 var role = container ? container.role : node.role;
305 return [node.attributes.name, node.attributes.value, role].join(', ');
306 }
307
308 // Walk the range and collect descriptions.
309 var output = '';
310 var cursor = range.getStart();
311 var nodeLocations = [];
312 while (cursor.getNode() != range.getEnd().getNode()) {
313 output += getCursorDesc(cursor);
314 nodeLocations.push(cursor.getNode().location);
315 cursor = cursor.move(
316 cursors.Unit.NODE, cursors.Movement.DIRECTIONAL, Dir.FORWARD);
317 }
318 output += getCursorDesc(range.getEnd());
319 nodeLocations.push(range.getEnd().getNode().location);
320
321 cvox.ChromeVox.tts.speak(output, cvox.QueueMode.FLUSH);
322 cvox.ChromeVox.braille.write(cvox.NavBraille.fromText(output));
323 chrome.accessibilityPrivate.setFocusRing(nodeLocations);
297 } 324 }
298 }; 325 };
299 326
300 /** @type {Background} */ 327 /** @type {Background} */
301 global.backgroundObj = new Background(); 328 global.backgroundObj = new Background();
302 329
303 }); // goog.scope 330 }); // goog.scope
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698