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

Unified Diff: chrome/browser/resources/chromeos/chromevox/cvox2/background/output.js

Issue 2487043002: Refine braille output (Closed)
Patch Set: Add additional test. Created 4 years, 1 month 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 side-by-side diff with in-line comments
Download patch
Index: chrome/browser/resources/chromeos/chromevox/cvox2/background/output.js
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/output.js b/chrome/browser/resources/chromeos/chromevox/cvox2/background/output.js
index 84e99215d7150a3dc046190cebad4af40f8d3f1d..cdcdbc92e61984189169d7f240b76368ceec4c6b 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/output.js
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/output.js
@@ -650,14 +650,12 @@ Output.EarconAction.prototype = {
* Annotation for text with a selection inside it.
* @param {number} startIndex
* @param {number} endIndex
- * @param {number=} opt_offset
* @constructor
*/
-Output.SelectionSpan = function(startIndex, endIndex, opt_offset) {
+Output.SelectionSpan = function(startIndex, endIndex) {
// TODO(dtseng): Direction lost below; should preserve for braille panning.
this.startIndex = startIndex < endIndex ? startIndex : endIndex;
this.endIndex = endIndex > startIndex ? endIndex : startIndex;
- this.offset = opt_offset || 0;
};
/**
@@ -872,7 +870,6 @@ Output.prototype = {
this.formatOptions_ = {speech: false, braille: true, auralStyle: false};
this.format_(node, formatStr, this.brailleBuffer_);
-
return this;
},
@@ -932,7 +929,7 @@ Output.prototype = {
// Braille.
if (this.brailleBuffer_.length) {
- var buff = this.createBrailleOutput_();
+ var buff = this.mergeBraille_(this.brailleBuffer_);
var selSpan =
buff.getSpanInstanceOf(Output.SelectionSpan);
var startIndex = -1, endIndex = -1;
@@ -941,7 +938,7 @@ Output.prototype = {
var valueEnd = buff.getSpanEnd(selSpan);
startIndex = valueStart + selSpan.startIndex;
endIndex = valueStart + selSpan.endIndex;
- buff.setSpan(new cvox.ValueSpan(selSpan.offset), valueStart, valueEnd);
+ buff.setSpan(new cvox.ValueSpan(0), valueStart, valueEnd);
buff.setSpan(new cvox.ValueSelectionSpan(), startIndex, endIndex);
}
@@ -1038,11 +1035,6 @@ Output.prototype = {
if (options.isUnique)
token = token.substring(0, token.length - 1);
- // Annotate braille output with the corresponding automation nodes
- // to support acting on nodes based on location in the output.
- if (node && this.formatOptions_.braille)
- options.annotation.push(new Output.NodeSpan(node));
-
// Process token based on prefix.
var prefix = token[0];
token = token.slice(1);
@@ -1068,7 +1060,7 @@ Output.prototype = {
options.annotation.push(token);
if (selectedText) {
this.append_(buff, selectedText, options);
- this.append_(buff, Msgs.getMsg('selected'), options);
+ this.append_(buff, Msgs.getMsg('selected'));
} else {
this.append_(buff, text, options);
}
@@ -1498,7 +1490,8 @@ Output.prototype = {
this.format_(formatPrevNode, roleBlock.leave, buff, prevNode);
}
- var enterOutputs = [];
+ // Customize for braille node annotations.
+ var originalBuff = buff;
var enterRole = {};
for (var j = uniqueAncestors.length - 1, formatNode;
(formatNode = uniqueAncestors[j]);
@@ -1507,8 +1500,18 @@ Output.prototype = {
if (roleBlock.enter) {
if (enterRole[formatNode.role])
continue;
+
+ if (this.formatOptions_.braille)
+ buff = [];
+
enterRole[formatNode.role] = true;
this.format_(formatNode, roleBlock.enter, buff, prevNode);
+
+ if (this.formatOptions_.braille && buff.length) {
+ var nodeSpan = this.mergeBraille_(buff);
+ nodeSpan.setSpan(new Output.NodeSpan(formatNode), 0, nodeSpan.length);
+ originalBuff.push(nodeSpan);
+ }
}
}
},
@@ -1521,6 +1524,11 @@ Output.prototype = {
* @private
*/
node_: function(node, prevNode, type, buff) {
+ var originalBuff = buff;
+
+ if (this.formatOptions_.braille)
+ buff = [];
+
// Navigate is the default event.
var eventBlock = Output.RULES[type] || Output.RULES['navigate'];
var roleBlock = eventBlock[node.role] || {};
@@ -1531,6 +1539,13 @@ Output.prototype = {
eventBlock['default'].speak;
this.format_(node, speakFormat, buff, prevNode);
+
+ // Restore braille and add an annotation for this node.
+ if (this.formatOptions_.braille) {
+ var nodeSpan = this.mergeBraille_(buff);
+ nodeSpan.setSpan(new Output.NodeSpan(node), 0, nodeSpan.length);
+ originalBuff.push(nodeSpan);
+ }
},
/**
@@ -1550,18 +1565,32 @@ Output.prototype = {
return;
var options = {annotation: ['name'], isUnique: true};
- var startIndex = range.start.index;
- var endIndex = range.end.index;
+ var rangeStart = range.start.index;
+ var rangeEnd = range.end.index;
if (this.formatOptions_.braille) {
options.annotation.push(new Output.NodeSpan(node));
var selStart = node.textSelStart;
var selEnd = node.textSelEnd;
+
if (selStart !== undefined &&
- (selEnd >= startIndex && selStart <= endIndex)) {
+ selEnd >= rangeStart && selStart <= rangeEnd) {
+ // Editable text selection.
+
+ // |rangeStart| and |rangeEnd| are indices set by the caller and are
+ // assumed to be inside of the range. In braille, we only ever expect to
+ // get ranges surrounding a line as anything smaller doesn't make sense.
+
+ // |selStart| and |selEnd| reflect the editable selection. The relative
+ // selStart and relative selEnd for the current line are then just the
+ // difference between |selStart|, |selEnd| with |rangeStart|.
+ // See editing_test.js for examples.
options.annotation.push(new Output.SelectionSpan(
- selStart - startIndex,
- selEnd - startIndex,
- startIndex));
+ selStart - rangeStart, selEnd - rangeStart));
+ } else if (rangeStart != 0 || rangeEnd != range.start.getText().length) {
+ // Non-editable text selection over less than the full contents covered
+ // by the range. We exclude full content underlines because it is
+ // distracting to read braille with all cells underlined with a cursor.
+ options.annotation.push(new Output.SelectionSpan(rangeStart, rangeEnd));
}
}
@@ -1570,13 +1599,24 @@ Output.prototype = {
var earcon = this.findEarcon_(node, prevNode);
if (earcon)
options.annotation.push(earcon);
- this.append_(buff, range.start.getText().substring(startIndex, endIndex),
- options);
+ var text = '';
+
+ if (this.formatOptions_.braille && !node.state.editable) {
+ // In braille, we almost always want to show the entire contents and
+ // simply place the cursor under the SelectionSpan we set above.
+ text = range.start.getText();
+ } else {
+ // This is output for speech or editable braille.
+ text = range.start.getText().substring(rangeStart, rangeEnd);
+ }
+
+ this.append_(buff, text, options);
+
if (!this.outputContextFirst_)
this.ancestry_(node, prevNode, type, buff);
var loc =
- range.start.node.boundsForRange(startIndex, endIndex);
+ range.start.node.boundsForRange(rangeStart, rangeEnd);
if (loc)
this.locations_.push(loc);
},
@@ -1673,65 +1713,49 @@ Output.prototype = {
},
/**
- * Converts the currently rendered braille buffers to a single spannable.
+ * Converts the braille |spans| buffer to a single spannable.
+ * @param {!Array<Spannable>} spans
* @return {!Spannable}
* @private
*/
- createBrailleOutput_: function() {
- var result = new Spannable();
+ mergeBraille_: function(spans) {
var separator = ''; // Changes to space as appropriate.
- this.brailleBuffer_.forEach(function(cur) {
- // If this chunk is empty, don't add it since it won't result
- // in any output on the braille display, but node spans would
- // start before the separator in that case, which is not desired.
- // The exception is if this chunk contains a selectionm, in which
- // case it will result in a cursor which has to be preserved.
- // In this case, having separators, potentially both before and after
- // the empty string is correct.
- if (cur.length == 0 && !cur.getSpanInstanceOf(Output.SelectionSpan))
- return;
- var spansToExtend = [];
- var spansToRemove = [];
- // Nodes that have node spans both on the character to the left
- // of the separator and to the right should also cover the separator.
- // We extend the left span to cover both the separator and what the
- // right span used to cover, removing the right span, mostly for
- // ease of writing tests and debug.
- // Note that getSpan(position) never returns zero length spans
- // (because they don't cover any position). Still, we want to include
- // these because they can be included (the selection span in an empty
- // text field is an example), which is why we write the below code
- // using getSpansInstanceOf and check the endpoints (isntead of doing
- // the opposite).
- result.getSpansInstanceOf(Output.NodeSpan).forEach(function(leftSpan) {
- if (result.getSpanEnd(leftSpan) < result.length)
- return;
- var newEnd = result.length;
- cur.getSpansInstanceOf(Output.NodeSpan).forEach(function(rightSpan) {
- if (cur.getSpanStart(rightSpan) == 0 &&
- leftSpan.node === rightSpan.node) {
- newEnd = Math.max(
- newEnd,
- result.length + separator.length +
- cur.getSpanEnd(rightSpan));
- spansToRemove.push(rightSpan);
- }
- });
- if (newEnd > result.length)
- spansToExtend.push({span: leftSpan, end: newEnd});
- });
+ var prevHasInlineNode = false;
+ return spans.reduce(function(result, cur) {
+ // Ignore empty spans except when they contain a selection.
+ var hasSelection = cur.getSpanInstanceOf(Output.SelectionSpan);
+ if (cur.length == 0 && !hasSelection)
+ return result;
+
+ // For empty selections, we just add the space separator to account for
+ // showing the braille cursor.
+ if (cur.length == 0 && hasSelection) {
+ result.append(cur);
+ result.append(Output.SPACE);
+ separator = '';
+ return result;
+ }
+
+ // Keep track of if there's an inline node associated with
+ // |cur|.
+ var hasInlineNode = cur.getSpansInstanceOf(Output.NodeSpan)
+ .some(function(s) {
+ return s.node && s.node.display == 'inline';
+ });
+
+ // Now, decide whether we should include separators between the previous
+ // span and |cur|.
+ // Never separate chunks without something already there at this point.
+ if (result.length == 0 || hasInlineNode || prevHasInlineNode)
+ separator = '';
+ else
+ separator = Output.SPACE;
+
+ prevHasInlineNode = hasInlineNode;
result.append(separator);
result.append(cur);
- spansToExtend.forEach(function(elem) {
- result.setSpan(
- elem.span,
- result.getSpanStart(elem.span),
- elem.end);
- });
- spansToRemove.forEach(result.removeSpan.bind(result));
- separator = Output.SPACE;
- });
- return result;
+ return result;
+ }, new Spannable());
},
/**
@@ -1796,7 +1820,7 @@ Output.prototype = {
* @return {!Spannable}
*/
get brailleOutputForTest() {
- return this.createBrailleOutput_();
+ return this.mergeBraille_(this.brailleBuffer_);
}
};

Powered by Google App Engine
This is Rietveld 408576698