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 a07c9049b60bc9a84c4ab7e2d79e36e55742c34b..34a4f699a65959ab011d333545a5625dc20e3d7d 100644 |
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/editing.js |
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/editing.js |
@@ -227,8 +227,14 @@ function AutomationRichEditableText(node) { |
if (!root || !root.anchorObject || !root.focusObject) |
return; |
- this.line_ = new editing.EditableLine( |
- root.anchorObject, root.anchorOffset, root.focusObject, root.focusOffset); |
+ this.anchorLine_ = new editing.EditableLine(root.anchorObject, |
dmazzoni
2017/06/19 15:11:51
Is the plan to refactor EditableLine to only take
David Tseng
2017/06/19 18:27:47
No, added comments to clarify. An editable line co
|
+ root.anchorOffset, |
+ root.anchorObject, |
+ root.anchorOffset); |
+ this.focusLine_ = new editing.EditableLine(root.focusObject, |
+ root.focusOffset, |
+ root.focusObject, |
+ root.focusOffset); |
} |
AutomationRichEditableText.prototype = { |
@@ -240,14 +246,34 @@ AutomationRichEditableText.prototype = { |
if (!root.anchorObject || !root.focusObject) |
return; |
+ var anchorLine = new editing.EditableLine(root.anchorObject, |
+ root.anchorOffset, |
+ root.anchorObject, |
+ root.anchorOffset); |
+ var focusLine = new editing.EditableLine(root.focusObject, |
+ root.focusOffset, |
+ root.focusObject, |
+ root.focusOffset); |
+ |
+ var prevAnchorLine = this.anchorLine_; |
+ var prevFocusLine = this.focusLine_; |
+ this.anchorLine_ = anchorLine; |
+ this.focusLine_ = focusLine; |
+ |
+ // Compute the current line based upon whether the current selection was |
+ // extended from anchor or focus. The default behavior is to compute lines |
+ // via focus. |
+ var baseLineOnStart = prevFocusLine.strictEquals(focusLine); |
+ |
var cur = new editing.EditableLine( |
root.anchorObject, root.anchorOffset || 0, |
- root.focusObject, root.focusOffset || 0); |
- var prev = this.line_; |
+ root.focusObject, root.focusOffset || 0, |
+ baseLineOnStart); |
this.line_ = cur; |
- if (prev.equals(cur)) { |
- // Collapsed cursor. |
+ // Selection stayed within the same line(s) and didn't cross into new lines. |
+ if (anchorLine.equals(prevAnchorLine) && focusLine.equals(prevFocusLine)) { |
+ // Intra-line changes. |
this.changed(new cvox.TextChangeEvent( |
cur.text || '', |
cur.startOffset || 0, |
@@ -295,9 +321,24 @@ AutomationRichEditableText.prototype = { |
return; |
} |
- // Just output the current line. |
- cvox.ChromeVox.tts.speak(cur.text, cvox.QueueMode.CATEGORY_FLUSH); |
- this.brailleCurrentRichLine_(); |
+ if (anchorLine.equals(focusLine)) { |
+ // Current selection is on a new line not part of the previous selection. |
+ this.line_ = focusLine; |
+ cvox.ChromeVox.tts.speak(focusLine.text, cvox.QueueMode.CATEGORY_FLUSH); |
+ this.brailleCurrentRichLine_(); |
+ } if (prevAnchorLine.equals(anchorLine)) { |
+ // The selection changed in focus position. |
+ this.line_ = focusLine; |
+ cvox.ChromeVox.tts.speak(focusLine.text, cvox.QueueMode.CATEGORY_FLUSH); |
+ this.brailleCurrentRichLine_(); |
+ } else if (prevFocusLine.equals(focusLine)) { |
+ // The selection changed in anchor position. |
+ this.line_ = anchorLine; |
+ cvox.ChromeVox.tts.speak(anchorLine.text, cvox.QueueMode.CATEGORY_FLUSH); |
+ this.brailleCurrentRichLine_(); |
+ } else { |
+ // Describe a generic DOM selection. |
dmazzoni
2017/06/19 15:11:51
Want to put some sort of placeholder here?
Or may
David Tseng
2017/06/19 18:27:47
Removed. We don't want to favor focus but favor th
|
+ } |
// The state in EditableTextBase needs to get updated with the new line |
// contents, so that subsequent intra-line changes get the right state |
@@ -455,9 +496,15 @@ editing.observer_ = new editing.EditingChromeVoxStateObserver(); |
/** |
* An EditableLine encapsulates all data concerning a line in the automation |
* tree necessary to provide output. |
+ * @param {AutomationNode} startNode |
+ * @param {number} startIndex |
+ * @param {AutomationNode} endNode |
+ * @param {number} endIndex |
+ * @param {boolean=} opt_baseLineOnStart |
* @constructor |
*/ |
-editing.EditableLine = function(startNode, startIndex, endNode, endIndex) { |
+editing.EditableLine = |
+ function(startNode, startIndex, endNode, endIndex, opt_baseLineOnStart) { |
/** @private {!Cursor} */ |
this.start_ = new Cursor(startNode, startIndex); |
this.start_ = this.start_.deepEquivalent || this.start_; |
@@ -481,26 +528,38 @@ editing.EditableLine = function(startNode, startIndex, endNode, endIndex) { |
this.lineStartContainer_; |
/** @private {number} */ |
this.localLineStartContainerOffset_ = 0; |
+ /** @private {AutomationNode|undefined} */ |
+ this.lineEndContainer_; |
+ /** @private {number} */ |
+ this.localLineEndContainerOffset_ = 0; |
- this.computeLineData_(); |
+ this.computeLineData_(opt_baseLineOnStart); |
}; |
editing.EditableLine.prototype = { |
/** @private */ |
- computeLineData_: function() { |
+ computeLineData_: function(opt_baseLineOnStart) { |
+ // Note that we calculate the line based only upon anchor or focus even if |
+ // they do not fall on the same line. It is up to the caller to specify |
+ // which end to base this line upon since it requires reasoning about two |
+ // lines. |
var nameLen = 0; |
- if (this.start_.node.name) |
- nameLen = this.start_.node.name.length; |
+ var lineBase = opt_baseLineOnStart ? this.start_ : this.end_; |
+ var lineExtend = opt_baseLineOnStart ? this.end_ : this.start_; |
+ |
+ if (lineBase.node.name) |
+ nameLen = lineBase.node.name.length; |
- this.value_ = new Spannable(this.start_.node.name || '', this.start_); |
- if (this.start_.node == this.end_.node) |
- this.value_.setSpan(this.end_, 0, nameLen); |
+ this.value_ = new Spannable(lineBase.node.name || '', lineBase); |
+ if (lineBase.node == lineExtend.node) |
+ this.value_.setSpan(lineExtend, 0, nameLen); |
// Initialize defaults. |
- this.lineStart_ = this.start_.node; |
+ this.lineStart_ = lineBase.node; |
this.lineEnd_ = this.lineStart_; |
this.startContainer_ = this.lineStart_.parent; |
this.lineStartContainer_ = this.lineStart_.parent; |
+ this.lineEndContainer_ = this.lineStart_.parent; |
// Annotate each chunk with its associated inline text box node. |
this.value_.setSpan(this.lineStart_, 0, this.lineStart_.name.length); |
@@ -547,6 +606,7 @@ editing.EditableLine.prototype = { |
this.value_.append(new Spannable(lineEnd.name, annotation)); |
} |
+ this.lineEndContainer_ = this.lineEnd_.parent; |
// Finally, annotate with all parent static texts as NodeSpan's so that |
// braille routing can key properly into the node with an offset. |
@@ -566,6 +626,11 @@ editing.EditableLine.prototype = { |
textCountAfterLineEnd += finder.name.length; |
} |
+ if (this.lineEndContainer_.name) { |
+ this.localLineEndContainerOffset_ = |
+ this.lineEndContainer_.name.length - textCountAfterLineEnd; |
+ } |
+ |
var len = 0; |
for (var i = 0; i < parents.length; i++) { |
var parent = parents[i]; |
@@ -605,7 +670,14 @@ editing.EditableLine.prototype = { |
* @return {number} |
*/ |
get startOffset() { |
- return this.value_.getSpanStart(this.start_) + this.start_.index; |
+ // It is possible that the start cursor points to content before this line |
+ // (e.g. in a multi-line selection). |
+ try { |
+ return this.value_.getSpanStart(this.start_) + this.start_.index; |
+ } catch (e) { |
+ // When that happens, fall back to the start of this line. |
+ return 0; |
+ } |
}, |
/** |
@@ -613,7 +685,11 @@ editing.EditableLine.prototype = { |
* @return {number} |
*/ |
get endOffset() { |
- return this.value_.getSpanStart(this.end_) + this.end_.index; |
+ try { |
+ return this.value_.getSpanStart(this.end_) + this.end_.index; |
+ } catch (e) { |
+ return this.value_.length - 1; |
+ } |
}, |
/** |
@@ -660,6 +736,21 @@ editing.EditableLine.prototype = { |
return this.value_.toString(); |
}, |
+ /** |
+ * |
+ */ |
+ equalsStart: function(otherLine) { |
+ return otherLine.lineStartContainer_ == this.lineStartContainer_ && |
+ otherLine.localLineStartContainerOffset_ == |
+ this.localLineStartContainerOffset_; |
+ }, |
+ |
+ equalsEnd: function(otherLine) { |
+ return otherLine.lineEndContainer_ == this.lineEndContainer_ && |
+ otherLine.localLineEndContainerOffset_ == |
+ this.localLineEndContainerOffset_; |
+ }, |
+ |
/** |
* Returns true if |otherLine| surrounds the same line as |this|. Note that |
* the contents of the line might be different. |
@@ -669,9 +760,13 @@ editing.EditableLine.prototype = { |
// Equality is intentionally loose here as any of the state nodes can be |
// invalidated at any time. We rely upon the start/anchor of the line |
// staying the same. |
- return otherLine.lineStartContainer_ == this.lineStartContainer_ && |
- otherLine.localLineStartContainerOffset_ == |
- this.localLineStartContainerOffset_; |
+ return this.equalsStart(otherLine) || this.equalsEnd(otherLine); |
+ }, |
+ |
+ strictEquals: function(otherLine) { |
+ return this.equals(otherLine) && |
+ this.startOffset == otherLine.startOffset && |
+ this.endOffset == otherLine.endOffset; |
} |
}; |