| Index: chrome/browser/resources/chromeos/chromevox/cvox2/background/editing.js
|
| diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/editing.js b/chrome/browser/resources/chromeos/chromevox/cvox2/background/editing.js
|
| index 199e4065051218e14740693862f0c68e14530339..1b5ee25d9906ed8f6d957da4b36c8d9fb9b6af26 100644
|
| --- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/editing.js
|
| +++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/editing.js
|
| @@ -253,10 +253,18 @@ AutomationRichEditableText.prototype = {
|
| // extended from anchor or focus. The default behavior is to compute lines
|
| // via focus.
|
| var baseLineOnStart = prevFocusLine.isSameLineAndSelection(focusLine);
|
| + var isSameSelection =
|
| + baseLineOnStart && prevAnchorLine.isSameLineAndSelection(anchorLine);
|
|
|
| - var cur = new editing.EditableLine(
|
| - root.anchorObject, root.anchorOffset, root.focusObject,
|
| - root.focusOffset, baseLineOnStart);
|
| + var cur;
|
| + if (isSameSelection && this.line_) {
|
| + // Nothing changed, return.
|
| + return;
|
| + } else {
|
| + cur = new editing.EditableLine(
|
| + root.anchorObject, root.anchorOffset, root.focusObject,
|
| + root.focusOffset, baseLineOnStart);
|
| + }
|
| var prev = this.line_;
|
| this.line_ = cur;
|
|
|
| @@ -317,8 +325,64 @@ AutomationRichEditableText.prototype = {
|
| .go();
|
| } else if (!cur.hasCollapsedSelection()) {
|
| // This is a selection.
|
| - cvox.ChromeVox.tts.speak(cur.selectedText, cvox.QueueMode.CATEGORY_FLUSH);
|
| - cvox.ChromeVox.tts.speak(Msgs.getMsg('selected'), cvox.QueueMode.QUEUE);
|
| +
|
| + // Speech requires many more states than braille.
|
| + // TODO(dtseng): base/extent and anchor/focus are mismatched in AX. Swap
|
| + // once this works.
|
| + var curBase = baseLineOnStart ? focusLine : anchorLine;
|
| + var curExtent = baseLineOnStart ? anchorLine : focusLine;
|
| + var text = '';
|
| + var suffixMsg = '';
|
| + if (curBase.isBeforeLine(curExtent)) {
|
| + // Forward selection.
|
| + if (prev.isBeforeLine(curBase)) {
|
| + // Wrapped across the baseline. Read out the new selection.
|
| + suffixMsg = 'selected';
|
| + text = this.getTextSelection_(
|
| + curBase.startContainer_, curBase.localStartOffset,
|
| + curExtent.endContainer_, curExtent.localEndOffset);
|
| + } else {
|
| + if (prev.isBeforeLine(curExtent)) {
|
| + // Grew.
|
| + suffixMsg = 'selected';
|
| + text = this.getTextSelection_(
|
| + prev.endContainer_, prev.localEndOffset,
|
| + curExtent.endContainer_, curExtent.localEndOffset);
|
| + } else {
|
| + // Shrank.
|
| + suffixMsg = 'unselected';
|
| + text = this.getTextSelection_(
|
| + curExtent.endContainer_, curExtent.localEndOffset,
|
| + prev.endContainer_, prev.localEndOffset);
|
| + }
|
| + }
|
| + } else {
|
| + // Backward selection.
|
| + if (curBase.isBeforeLine(prev)) {
|
| + // Wrapped across the baseline. Read out the new selection.
|
| + suffixMsg = 'selected';
|
| + text = this.getTextSelection_(
|
| + curExtent.startContainer_, curExtent.localStartOffset,
|
| + curBase.endContainer_, curBase.localEndOffset);
|
| + } else {
|
| + if (curExtent.isBeforeLine(prev)) {
|
| + // Grew.
|
| + suffixMsg = 'selected';
|
| + text = this.getTextSelection_(
|
| + curExtent.startContainer_, curExtent.localStartOffset,
|
| + prev.startContainer_, prev.localStartOffset);
|
| + } else {
|
| + // Shrank.
|
| + suffixMsg = 'unselected';
|
| + text = this.getTextSelection_(
|
| + prev.startContainer_, prev.localStartOffset,
|
| + curExtent.startContainer_, curExtent.localStartOffset);
|
| + }
|
| + }
|
| + }
|
| +
|
| + cvox.ChromeVox.tts.speak(text, cvox.QueueMode.CATEGORY_FLUSH);
|
| + cvox.ChromeVox.tts.speak(Msgs.getMsg(suffixMsg), cvox.QueueMode.QUEUE);
|
| this.brailleCurrentRichLine_();
|
| } else {
|
| // Describe the current line. This accounts for previous/current
|
| @@ -336,6 +400,40 @@ AutomationRichEditableText.prototype = {
|
| this.end = cur.endOffset;
|
| },
|
|
|
| + /**
|
| + * @param {AutomationNode|undefined} startNode
|
| + * @param {number} startOffset
|
| + * @param {AutomationNode|undefined} endNode
|
| + * @param {number} endOffset
|
| + * @return {string}
|
| + */
|
| + getTextSelection_: function(startNode, startOffset, endNode, endOffset) {
|
| + if (!startNode || !endNode)
|
| + return '';
|
| +
|
| + if (startNode == endNode) {
|
| + return startNode.name ? startNode.name.substring(startOffset, endOffset) :
|
| + '';
|
| + }
|
| +
|
| + var text = '';
|
| + if (startNode.name)
|
| + text = startNode.name.substring(startOffset);
|
| +
|
| + for (var node = startNode;
|
| + (node = AutomationUtil.findNextNode(
|
| + node, Dir.FORWARD, AutomationPredicate.leafOrStaticText)) &&
|
| + node != endNode;) {
|
| + // Padding needs to get added to break up speech utterances.
|
| + if (node.name)
|
| + text += ' ' + node.name;
|
| + }
|
| +
|
| + if (endNode.name)
|
| + text += ' ' + endNode.name.substring(0, endOffset);
|
| + return text;
|
| + },
|
| +
|
| /**
|
| * @param {number} markerType
|
| * @param {boolean=} opt_end
|
| @@ -543,6 +641,8 @@ editing.EditableLine = function(
|
| this.end_ = this.end_.deepEquivalent || this.end_;
|
| /** @private {number} */
|
| this.localContainerStartOffset_ = startIndex;
|
| + /** @private {number} */
|
| + this.localContainerEndOffset_ = endIndex;
|
|
|
| // Computed members.
|
| /** @private {Spannable} */
|
| @@ -583,10 +683,16 @@ editing.EditableLine.prototype = {
|
| if (lineBase.node == lineExtend.node)
|
| this.value_.setSpan(lineExtend, 0, nameLen);
|
|
|
| + this.startContainer_ = this.start_.node;
|
| + if (this.startContainer_.role == RoleType.INLINE_TEXT_BOX)
|
| + this.startContainer_ = this.startContainer_.parent;
|
| + this.endContainer_ = this.end_.node;
|
| + if (this.endContainer_.role == RoleType.INLINE_TEXT_BOX)
|
| + this.endContainer_ = this.endContainer_.parent;
|
| +
|
| // Initialize defaults.
|
| this.lineStart_ = lineBase.node;
|
| this.lineEnd_ = this.lineStart_;
|
| - this.startContainer_ = this.lineStart_.parent;
|
| this.lineStartContainer_ = this.lineStart_.parent;
|
| this.lineEndContainer_ = this.lineStart_.parent;
|
|
|
| @@ -739,7 +845,7 @@ editing.EditableLine.prototype = {
|
| * @return {number}
|
| */
|
| get localStartOffset() {
|
| - return this.startOffset - this.containerStartOffset;
|
| + return this.localContainerStartOffset_;
|
| },
|
|
|
| /**
|
| @@ -748,7 +854,7 @@ editing.EditableLine.prototype = {
|
| * @return {number}
|
| */
|
| get localEndOffset() {
|
| - return this.endOffset - this.containerStartOffset;
|
| + return this.localContainerEndOffset_;
|
| },
|
|
|
| /**
|
| @@ -815,6 +921,19 @@ editing.EditableLine.prototype = {
|
| return this.isSameLine(otherLine) &&
|
| this.startOffset == otherLine.startOffset &&
|
| this.endOffset == otherLine.endOffset;
|
| + },
|
| +
|
| + /**
|
| + * Returns whether this line comes before |otherLine| in document order.
|
| + * @return {boolean}
|
| + */
|
| + isBeforeLine: function(otherLine) {
|
| + if (this.isSameLine(otherLine) || !this.lineStartContainer_ ||
|
| + !otherLine.lineStartContainer_)
|
| + return false;
|
| + return AutomationUtil.getDirection(
|
| + this.lineStartContainer_, otherLine.lineStartContainer_) ==
|
| + Dir.FORWARD;
|
| }
|
| };
|
|
|
|
|