Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / resources / chromeos / chromevox / walkers / layout_line_walker.js
blobb46a089df197de8e9956fffd09bd5fcd190aca33
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 /**
6  * @fileoverview A JavaScript class for walking lines consisting of one or more
7  * clickable nodes.
8  */
11 goog.provide('cvox.LayoutLineWalker');
13 goog.require('cvox.AbstractWalker');
14 goog.require('cvox.StructuralLineWalker');
17 /**
18  * @constructor
19  * @extends {cvox.AbstractWalker}
20  */
21 cvox.LayoutLineWalker = function() {
22   this.subWalker_ = new cvox.StructuralLineWalker();
24 goog.inherits(cvox.LayoutLineWalker, cvox.AbstractWalker);
27 /**
28  * @override
29  */
30 cvox.LayoutLineWalker.prototype.next = function(sel) {
31   // Collapse selection to the directed end.
32   var endSel = new cvox.CursorSelection(sel.end, sel.end, sel.isReversed());
34   // Sync to the line.
35   var sync = this.subWalker_.sync(endSel);
36   if (!sync) {
37     return null;
38   }
40   // Compute the next selection.
41   var start = this.subWalker_.next(endSel);
42   if (!start) {
43     return null;
44   }
45   start.setReversed(sel.isReversed());
46   return this.extend_(start).setReversed(false);
50 /**
51  * @override
52  */
53 cvox.LayoutLineWalker.prototype.sync = function(sel) {
54   var line = this.subWalker_.sync(sel);
55   if (!line) {
56     return null;
57   }
59   // Extend to both line breaks (in each direction).
60   var end = this.extend_(line);
61   var start = this.extend_(line.setReversed(!line.isReversed()));
63   return new cvox.CursorSelection(start.end, end.end, sel.isReversed());
67 /**
68  * @override
69  */
70 cvox.LayoutLineWalker.prototype.getDescription = function(prevSel, sel) {
71   var descriptions = [];
72   var prev = prevSel;
73   var absSel = sel.clone().setReversed(false);
74   var cur = new cvox.CursorSelection(absSel.start, absSel.start);
75   cur = this.subWalker_.sync(cur);
76   if (!cur) {
77     return [];
78   }
80   // No need to accumulate descriptions.
81   if (absSel.start.node == absSel.end.node) {
82     return this.subWalker_.getDescription(prevSel, sel);
83   }
85   // Walk through and collect descriptions for each line.
86   while (cur && !cur.end.equals(absSel.end)) {
87     descriptions.push.apply(
88         descriptions, this.subWalker_.getDescription(prev, cur));
89     prev = cur;
90     cur = this.subWalker_.next(cur);
91   }
92   if (cur) {
93     descriptions.push.apply(
94         descriptions, this.subWalker_.getDescription(prev, cur));
95   }
96   return descriptions;
101  * @override
102  */
103 cvox.LayoutLineWalker.prototype.getBraille = function(prevSel, sel) {
104   var braille = new cvox.NavBraille({});
105   var absSel = this.subWalker_.sync(sel.clone().setReversed(false));
106   var layoutSel = this.sync(sel).setReversed(false);
107   if (!layoutSel || !absSel) {
108     return braille;
109   }
110   var cur = new cvox.CursorSelection(layoutSel.start, layoutSel.start);
111   cur = this.subWalker_.sync(cur);
112   if (!cur) {
113     return braille;
114   }
116   // Walk through and collect braille for each line.
117   while (cur && !cur.end.equals(layoutSel.end)) {
118     this.appendBraille_(prevSel, absSel, cur, braille);
119     prevSel = cur;
120     cur = this.subWalker_.next(cur);
121   }
122   if (cur) {
123     this.appendBraille_(prevSel, absSel, cur, braille);
124   }
125   return braille;
130  * @override
131  */
132 cvox.LayoutLineWalker.prototype.getGranularityMsg = function() {
133   return cvox.ChromeVox.msgs.getMsg('layout_line');
138  * Compares two selections and determines if the lie on the same horizontal
139  * line as determined by their bounding rectangles.
140  * @param {!cvox.CursorSelection} lSel Left selection.
141  * @param {!cvox.CursorSelection} rSel Right selection.
142  * @return {boolean} Whether lSel and rSel are on different visual lines.
143  * @private
144  */
145 cvox.LayoutLineWalker.prototype.isVisualLineBreak_ = function(lSel, rSel) {
146   if (this.wantsOwnLine_(lSel.end.node) ||
147       this.wantsOwnLine_(rSel.start.node)) {
148     return true;
149   }
150   var lRect = lSel.getRange().getBoundingClientRect();
151   var rRect = rSel.getRange().getBoundingClientRect();
153   // Some ranges from the browser give us 0-sized rects (such as in the case of
154   // select's). Detect these cases and use a more reliable method (take the
155   // bounding rect of the actual element rather than the range).
156   if (lRect.width == 0 &&
157       lRect.height == 0 &&
158       lSel.end.node.nodeType == Node.ELEMENT_NODE) {
159     lRect = lSel.end.node.getBoundingClientRect();
160   }
162   if (rRect.width == 0 &&
163       rRect.height == 0 &&
164       rSel.start.node.nodeType == Node.ELEMENT_NODE) {
165     rRect = rSel.start.node.getBoundingClientRect();
166   }
167   return lRect.bottom != rRect.bottom;
172  * Determines if node should force a line break.
173  * This is used for elements with unusual semantics, such as multi-line
174  * text fields, where the behaviour would otherwise be confusing.
175  * @param {!Node} node Node.
176  * @return {boolean} True if the node should appear next to a line break.
177  * @private
178  */
179 cvox.LayoutLineWalker.prototype.wantsOwnLine_ = function(node) {
180   return node instanceof HTMLTextAreaElement ||
181       node.parentNode instanceof HTMLTextAreaElement;
186  * Extends a given cursor selection up to the next visual line break.
187  * @param {!cvox.CursorSelection} start The selection.
188  * @return {!cvox.CursorSelection} The resulting selection.
189  * @private
190  */
191 cvox.LayoutLineWalker.prototype.extend_ = function(start) {
192   // Extend the selection up to just before a new visual line break.
193   var end = start;
194   var next = start;
196   do {
197     end = next;
198     next = this.subWalker_.next(end);
199   } while (next && !this.isVisualLineBreak_(end, next));
200   return new cvox.CursorSelection(start.start, end.end, start.isReversed());
205  * Private routine to append braille given three selections.
206  * @param {!cvox.CursorSelection} prevSel A previous selection in walker
207  * ordering.
208  * @param {!cvox.CursorSelection} sel A selection that represents the location
209  * of the braille cursor.
210  * @param {!cvox.CursorSelection} cur The specific selection to append.
211  * @param {!cvox.NavBraille} braille Braille on which to append.
212  * @private
213  */
214 cvox.LayoutLineWalker.prototype.appendBraille_ = function(
215     prevSel, sel, cur, braille) {
216   var item = this.subWalker_.getBraille(prevSel, cur).text;
217   var valueSelectionSpan = item.getSpanInstanceOf(cvox.ValueSelectionSpan);
219   if (braille.text.getLength() > 0) {
220     braille.text.append(cvox.BrailleUtil.ITEM_SEPARATOR);
221   }
223   // Find the surrounding logical "leaf node".
224   // This prevents us from labelling the braille output with the wrong node,
225   // such as a text node child of a <textarea>.
226   var node = cur.start.node;
227   while (node.parentNode && cvox.DomUtil.isLeafNode(node.parentNode)) {
228     node = node.parentNode;
229   }
231   var nodeStart = braille.text.getLength();
232   var nodeEnd = nodeStart + item.getLength();
233   braille.text.append(item);
234   braille.text.setSpan(node, nodeStart, nodeEnd);
236   if (sel && cur.absEquals(sel)) {
237     if (valueSelectionSpan) {
238       braille.startIndex = nodeStart + item.getSpanStart(valueSelectionSpan);
239       braille.endIndex = nodeStart + item.getSpanEnd(valueSelectionSpan);
240     } else {
241       braille.startIndex = nodeStart;
242       braille.endIndex = nodeStart + 1;
243     }
244   }