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; |
} |
}; |