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

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

Issue 2540553002: Implement rich editable line output (Closed)
Patch Set: Fix plain text line braille. Created 3 years, 7 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 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 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 Processes events related to editing text and emits the 6 * @fileoverview Processes events related to editing text and emits the
7 * appropriate spoken and braille feedback. 7 * appropriate spoken and braille feedback.
8 */ 8 */
9 9
10 goog.provide('editing.TextEditHandler'); 10 goog.provide('editing.TextEditHandler');
11 11
12 goog.require('AutomationTreeWalker'); 12 goog.require('AutomationTreeWalker');
13 goog.require('AutomationUtil'); 13 goog.require('AutomationUtil');
14 goog.require('Output'); 14 goog.require('Output');
15 goog.require('Output.EventType'); 15 goog.require('Output.EventType');
16 goog.require('cursors.Cursor'); 16 goog.require('cursors.Cursor');
17 goog.require('cursors.Range'); 17 goog.require('cursors.Range');
18 goog.require('cvox.BrailleBackground'); 18 goog.require('cvox.BrailleBackground');
19 goog.require('cvox.ChromeVoxEditableTextBase'); 19 goog.require('cvox.ChromeVoxEditableTextBase');
20 20
21 goog.scope(function() { 21 goog.scope(function() {
22 var AutomationEvent = chrome.automation.AutomationEvent; 22 var AutomationEvent = chrome.automation.AutomationEvent;
23 var AutomationNode = chrome.automation.AutomationNode; 23 var AutomationNode = chrome.automation.AutomationNode;
24 var Cursor = cursors.Cursor; 24 var Cursor = cursors.Cursor;
25 var Dir = AutomationUtil.Dir; 25 var Dir = constants.Dir;
26 var EventType = chrome.automation.EventType; 26 var EventType = chrome.automation.EventType;
27 var Range = cursors.Range; 27 var Range = cursors.Range;
28 var RoleType = chrome.automation.RoleType; 28 var RoleType = chrome.automation.RoleType;
29 var StateType = chrome.automation.StateType; 29 var StateType = chrome.automation.StateType;
30 var Movement = cursors.Movement; 30 var Movement = cursors.Movement;
31 var Unit = cursors.Unit; 31 var Unit = cursors.Unit;
32 32
33 /** 33 /**
34 * A handler for automation events in a focused text field or editable root 34 * A handler for automation events in a focused text field or editable root
35 * such as a |contenteditable| subtree. 35 * such as a |contenteditable| subtree.
36 * @constructor 36 * @constructor
37 * @param {!AutomationNode} node 37 * @param {!AutomationNode} node
38 */ 38 */
39 editing.TextEditHandler = function(node) { 39 editing.TextEditHandler = function(node) {
40 /** @const {!AutomationNode} @private */ 40 /** @const {!AutomationNode} @private */
41 this.node_ = node; 41 this.node_ = node;
42 }; 42 };
43 43
44 /**
45 * Flag set to indicate whether ChromeVox uses experimental rich text support.
46 * @type {boolean}
47 */
48 editing.useRichText = false;
49
44 editing.TextEditHandler.prototype = { 50 editing.TextEditHandler.prototype = {
45 /** @return {!AutomationNode} */ 51 /** @return {!AutomationNode} */
46 get node() { 52 get node() {
47 return this.node_; 53 return this.node_;
48 }, 54 },
49 55
50 /** 56 /**
51 * Receives the following kinds of events when the node provided to the 57 * Receives the following kinds of events when the node provided to the
52 * constructor is focuse: |focus|, |textChanged|, |textSelectionChanged| and 58 * constructor is focuse: |focus|, |textChanged|, |textSelectionChanged| and
53 * |valueChanged|. 59 * |valueChanged|.
54 * An implementation of this method should emit the appropritate braille and 60 * An implementation of this method should emit the appropritate braille and
55 * spoken feedback for the event. 61 * spoken feedback for the event.
56 * @param {!(AutomationEvent|CustomAutomationEvent)} evt 62 * @param {!(AutomationEvent|CustomAutomationEvent)} evt
57 */ 63 */
58 onEvent: goog.abstractMethod, 64 onEvent: goog.abstractMethod,
59 }; 65 };
60 66
61 /** 67 /**
62 * A |TextEditHandler| suitable for text fields. 68 * A |TextEditHandler| suitable for text fields.
63 * @constructor 69 * @constructor
64 * @param {!AutomationNode} node A node with the role of |textField| 70 * @param {!AutomationNode} node A node with the role of |textField|
65 * @extends {editing.TextEditHandler} 71 * @extends {editing.TextEditHandler}
66 */ 72 */
67 function TextFieldTextEditHandler(node) { 73 function TextFieldTextEditHandler(node) {
68 editing.TextEditHandler.call(this, node); 74 editing.TextEditHandler.call(this, node);
69 /** @type {AutomationEditableText} @private */ 75
70 this.editableText_ = new AutomationEditableText(node); 76 chrome.automation.getDesktop(function(desktop) {
77 var useRichText = editing.useRichText &&
78 node.state.richlyEditable;
79
80 /** @private {!AutomationEditableText} */
81 this.editableText_ = useRichText ?
82 new AutomationRichEditableText(node) : new AutomationEditableText(node);
83 }.bind(this));
71 } 84 }
72 85
73 TextFieldTextEditHandler.prototype = { 86 TextFieldTextEditHandler.prototype = {
74 __proto__: editing.TextEditHandler.prototype, 87 __proto__: editing.TextEditHandler.prototype,
75 88
76 /** @override */ 89 /** @override */
77 onEvent: function(evt) { 90 onEvent: function(evt) {
78 if (evt.type !== EventType.TEXT_CHANGED && 91 if (evt.type !== EventType.TEXT_CHANGED &&
79 evt.type !== EventType.TEXT_SELECTION_CHANGED && 92 evt.type !== EventType.TEXT_SELECTION_CHANGED &&
80 evt.type !== EventType.VALUE_CHANGED && 93 evt.type !== EventType.VALUE_CHANGED &&
(...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after
192 } else { 205 } else {
193 range = Range.fromNode(this.node_); 206 range = Range.fromNode(this.node_);
194 } 207 }
195 output.withBraille(range, null, Output.EventType.NAVIGATE); 208 output.withBraille(range, null, Output.EventType.NAVIGATE);
196 if (isFirstLine) 209 if (isFirstLine)
197 output.formatForBraille('@tag_textarea'); 210 output.formatForBraille('@tag_textarea');
198 output.go(); 211 output.go();
199 } 212 }
200 }; 213 };
201 214
215
216 /**
217 * A |ChromeVoxEditableTextBase| that implements text editing feedback
218 * for automation tree text fields using anchor and focus selection.
219 * @constructor
220 * @param {!AutomationNode} node
221 * @extends {AutomationEditableText}
222 */
223 function AutomationRichEditableText(node) {
224 AutomationEditableText.call(this, node);
225
226 var root = this.node_.root;
227 if (!root || !root.anchorObject || !root.focusObject)
228 return;
229
230 this.range = new cursors.Range(
231 new cursors.Cursor(root.anchorObject, root.anchorOffset || 0),
232 new cursors.Cursor(root.focusObject, root.focusOffset || 0));
233 }
234
235 AutomationRichEditableText.prototype = {
236 __proto__: AutomationEditableText.prototype,
237
238 /** @override */
239 onUpdate: function() {
240 var root = this.node_.root;
241 if (!root.anchorObject || !root.focusObject)
242 return;
243
244 var cur = new cursors.Range(
245 new cursors.Cursor(root.anchorObject, root.anchorOffset || 0),
246 new cursors.Cursor(root.focusObject, root.focusOffset || 0));
247 var prev = this.range;
248
249 this.range = cur;
250
251 if (prev.start.node == cur.start.node &&
252 prev.end.node == cur.end.node &&
253 cur.start.node == cur.end.node) {
254 // Plain text: diff the two positions.
255 this.changed(new cvox.TextChangeEvent(
256 root.anchorObject.name || '',
257 root.anchorOffset || 0,
258 root.focusOffset || 0,
259 true));
260
261 var lineIndex = this.getLineIndex(this.start);
262 var brailleLineStart = this.getLineStart(lineIndex);
263 var brailleLineEnd = this.getLineEnd(lineIndex);
264 var buff = new Spannable(this.value);
265 buff.setSpan(new cvox.ValueSpan(0), brailleLineStart, brailleLineEnd);
266
267 var selStart = this.start - brailleLineStart;
268 var selEnd = this.end - brailleLineStart;
269 buff.setSpan(new cvox.ValueSelectionSpan(), selStart, selEnd);
270 cvox.ChromeVox.braille.write(new cvox.NavBraille({text: buff,
271 startIndex: selStart,
272 endIndex: selEnd}));
273 return;
274 } else {
275 // Rich text:
276 // If the position is collapsed, expand to the current line.
277 var start = cur.start;
278 var end = cur.end;
279 if (start.equals(end)) {
280 start = start.move(Unit.LINE, Movement.BOUND, Dir.BACKWARD);
281 end = end.move(Unit.LINE, Movement.BOUND, Dir.FORWARD);
282 }
283 var range = new cursors.Range(start, end);
284 var output = new Output().withRichSpeechAndBraille(
285 range, prev, Output.EventType.NAVIGATE);
286
287 // This position is not describable.
288 if (!output.hasSpeech) {
289 cvox.ChromeVox.tts.speak('blank', cvox.QueueMode.CATEGORY_FLUSH);
290 cvox.ChromeVox.braille.write(
291 new cvox.NavBraille({text: '', startIndex: 0, endIndex: 0}));
292 } else {
293 output.go();
294 }
295 }
296
297 // Keep the other members in sync for any future editable text base state
298 // machine changes.
299 this.value = cur.start.node.name || '';
300 this.start = cur.start.index;
301 this.end = cur.start.index;
302 },
303
304 /** @override */
305 describeSelectionChanged: function(evt) {
306 // Ignore end of text announcements.
307 if ((this.start + 1) == evt.start && evt.start == this.value.length)
308 return;
309
310 cvox.ChromeVoxEditableTextBase.prototype.describeSelectionChanged.call(
311 this, evt);
312 },
313
314 /** @override */
315 getLineIndex: function(charIndex) {
316 var breaks = this.getLineBreaks_();
317 var index = 0;
318 while (index < breaks.length && breaks[index] <= charIndex)
319 ++index;
320 return index;
321 },
322
323 /** @override */
324 getLineStart: function(lineIndex) {
325 if (lineIndex == 0)
326 return 0;
327 var breaks = this.getLineBreaks_();
328 return breaks[lineIndex - 1] ||
329 this.node_.root.focusObject.value.length;
330 },
331
332 /** @override */
333 getLineEnd: function(lineIndex) {
334 var breaks = this.getLineBreaks_();
335 var value = this.node_.root.focusObject.name;
336 if (lineIndex >= breaks.length)
337 return value.length;
338 return breaks[lineIndex];
339 },
340
341 /** @override */
342 getLineBreaks_: function() {
343 return this.node_.root.focusObject.lineStartOffsets || [];
344 }
345 };
346
202 /** 347 /**
203 * @param {!AutomationNode} node The root editable node, i.e. the root of a 348 * @param {!AutomationNode} node The root editable node, i.e. the root of a
204 * contenteditable subtree or a text field. 349 * contenteditable subtree or a text field.
205 * @return {editing.TextEditHandler} 350 * @return {editing.TextEditHandler}
206 */ 351 */
207 editing.TextEditHandler.createForNode = function(node) { 352 editing.TextEditHandler.createForNode = function(node) {
208 var rootFocusedEditable = null; 353 var rootFocusedEditable = null;
209 var testNode = node; 354 var testNode = node;
210 355
211 do { 356 do {
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
245 localStorage['brailleTable']); 390 localStorage['brailleTable']);
246 } 391 }
247 }; 392 };
248 393
249 /** 394 /**
250 * @private {ChromeVoxStateObserver} 395 * @private {ChromeVoxStateObserver}
251 */ 396 */
252 editing.observer_ = new editing.EditingChromeVoxStateObserver(); 397 editing.observer_ = new editing.EditingChromeVoxStateObserver();
253 398
254 }); 399 });
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698