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

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: Reintroduce test. 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.range_ = null;
dmazzoni 2014/10/17 16:46:08 Does this mean the current selected object? Maybe
Peter Lundblad 2014/10/23 13:35:07 Either make the name more self-explanatory or add
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.range_)
133 return; 133 return;
134 134
135 var previous = this.current_; 135 var current = this.range_.clone();
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.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.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.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.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.start.node.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 = AutomationUtil.findNodePost(current.start.node.root,
188 Dir.BACKWARD, 176 Dir.BACKWARD,
189 AutomationPredicate.inlineTextBox); 177 AutomationPredicate.leaf);
178 if (node)
179 current = cursors.Range.fromNode(node);
190 break; 180 break;
191 } 181 }
192 182
193 if (pred) 183 if (pred) {
194 current = AutomationUtil.findNextNode(current, dir, pred); 184 var node = AutomationUtil.findNextNode(
185 current.collapse(dir).start.node, dir, pred);
186
187 if (node)
188 current = cursors.Range.fromNode(node);
189 }
195 190
196 if (current) { 191 if (current) {
197 current.focus(); 192 current.start.node.focus();
198 193
199 this.onFocus({target: current}); 194 this.range_ = current;
195 this.handleOutput(this.range_);
200 } 196 }
201 }, 197 },
202 198
203 /** 199 /**
204 * Provides all feedback once ChromeVox's focus changes. 200 * Provides all feedback once ChromeVox's focus changes.
205 * @param {Object} evt 201 * @param {Object} evt
206 */ 202 */
207 onFocus: function(evt) { 203 onFocus: function(evt) {
208 var node = evt.target; 204 var node = evt.target;
209 if (!node) 205 if (!node)
210 return; 206 return;
211 207
212 this.current_ = node; 208 this.range_ = cursors.Range.fromNode(node);
213 var container = node; 209 this.handleOutput(this.range_);
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 }, 210 },
227 211
228 /** 212 /**
229 * Provides all feedback once a load complete event fires. 213 * Provides all feedback once a load complete event fires.
230 * @param {Object} evt 214 * @param {Object} evt
231 */ 215 */
232 onLoadComplete: function(evt) { 216 onLoadComplete: function(evt) {
233 if (this.current_) 217 if (this.range_)
234 return; 218 return;
235 219
236 this.current_ = AutomationUtil.findNodePost(evt.target, 220 var node = AutomationUtil.findNodePost(evt.target,
237 Dir.FORWARD, 221 Dir.FORWARD,
238 AutomationPredicate.inlineTextBox); 222 AutomationPredicate.leaf);
239 this.onFocus({target: this.current_}); 223 if (node)
224 this.range_ = cursors.Range.fromNode(node);
225
226 if (this.range_)
227 this.handleOutput(this.range_);
240 }, 228 },
241 229
242 /** 230 /**
243 * @private 231 * @private
244 * @param {string} url 232 * @param {string} url
245 * @return {boolean} Whether the given |url| is whitelisted. 233 * @return {boolean} Whether the given |url| is whitelisted.
246 */ 234 */
247 isWhitelisted_: function(url) { 235 isWhitelisted_: function(url) {
248 return this.whitelist_.some(function(item) { 236 return this.whitelist_.some(function(item) {
249 return url.indexOf(item) != -1; 237 return url.indexOf(item) != -1;
(...skipping 21 matching lines...) Expand all
271 opt_options.next = !this.active_; 259 opt_options.next = !this.active_;
272 opt_options.classic = !opt_options.next; 260 opt_options.classic = !opt_options.next;
273 } 261 }
274 262
275 if (opt_options.next) { 263 if (opt_options.next) {
276 chrome.automation.getTree(this.onGotTree); 264 chrome.automation.getTree(this.onGotTree);
277 this.active_ = true; 265 this.active_ = true;
278 } else { 266 } else {
279 if (this.active_) { 267 if (this.active_) {
280 for (var eventType in this.listeners_) { 268 for (var eventType in this.listeners_) {
281 this.current_.root.removeEventListener( 269 this.range_.start.node.root.removeEventListener(
282 eventType, this.listeners_[eventType], true); 270 eventType, this.listeners_[eventType], true);
283 } 271 }
284 } 272 }
285 this.active_ = false; 273 this.active_ = false;
286 } 274 }
287 275
288 chrome.tabs.query({active: true}, function(tabs) { 276 chrome.tabs.query({active: true}, function(tabs) {
289 if (opt_options.classic) { 277 if (opt_options.classic) {
290 cvox.ChromeVox.injectChromeVoxIntoTabs(tabs); 278 cvox.ChromeVox.injectChromeVoxIntoTabs(tabs);
291 } else { 279 } else {
292 tabs.forEach(function(tab) { 280 tabs.forEach(function(tab) {
293 this.disableClassicChromeVox_(tab.id); 281 this.disableClassicChromeVox_(tab.id);
294 }.bind(this)); 282 }.bind(this));
295 } 283 }
296 }.bind(this)); 284 }.bind(this));
285 },
286
287 /**
288 * Handles output of a Range.
289 * @param {!cursors.Range} range Current location.
290 */
291 handleOutput: function(range) {
292 // TODO(dtseng): This is just placeholder logic for generating descriptions
293 // pending further design discussion.
294 function getCursorDesc(cursor) {
295 var node = cursor.node;
296 var container = node;
297 while (container &&
298 (container.role == chrome.automation.RoleType.inlineTextBox ||
299 container.role == chrome.automation.RoleType.staticText))
300 container = container.parent();
301
302 var role = container ? container.role : node.role;
303
304 return [node.attributes.name, node.attributes.value, role].join(', ');
305 }
306
307 // Walk the range and collect descriptions.
308 var output = '';
309 var cursor = range.start.clone();
310 var nodeLocations = [];
311 while (cursor.node != range.end.node) {
312 output += getCursorDesc(cursor);
313 nodeLocations.push(cursor.node.location);
314 cursor.move(cursors.Unit.NODE, cursors.Movement.DIRECTIONAL, Dir.FORWARD);
315 }
316 output += getCursorDesc(range.end);
317 nodeLocations.push(range.end.node.location);
318
319 cvox.ChromeVox.tts.speak(output, cvox.QueueMode.FLUSH);
320 cvox.ChromeVox.braille.write(cvox.NavBraille.fromText(output));
321 chrome.accessibilityPrivate.setFocusRing([nodeLocations]);
297 } 322 }
298 }; 323 };
299 324
300 /** @type {Background} */ 325 /** @type {Background} */
301 global.backgroundObj = new Background(); 326 global.backgroundObj = new Background();
302 327
303 }); // goog.scope 328 }); // goog.scope
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698