OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 /** | 5 /** |
6 * @fileoverview Classes related to cursors that point to and select parts of | 6 * @fileoverview Classes related to cursors that point to and select parts of |
7 * the automation tree. | 7 * the automation tree. |
8 */ | 8 */ |
9 | 9 |
10 goog.provide('cursors.Cursor'); | 10 goog.provide('cursors.Cursor'); |
(...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
114 * Use this for loose equality between cursors where specific character-based | 114 * Use this for loose equality between cursors where specific character-based |
115 * indicies do not matter such as when processing node-targeted events. | 115 * indicies do not matter such as when processing node-targeted events. |
116 * @param {!cursors.Cursor} rhs | 116 * @param {!cursors.Cursor} rhs |
117 * @return {boolean} | 117 * @return {boolean} |
118 */ | 118 */ |
119 contentEquals: function(rhs) { | 119 contentEquals: function(rhs) { |
120 // First, normalize the two nodes so they both point to the first non-text | 120 // First, normalize the two nodes so they both point to the first non-text |
121 // node. | 121 // node. |
122 var lNode = this.node; | 122 var lNode = this.node; |
123 var rNode = rhs.node; | 123 var rNode = rhs.node; |
124 while (lNode && (lNode.role == RoleType.inlineTextBox || | 124 while (lNode && (lNode.role == RoleType.INLINE_TEXT_BOX || |
125 lNode.role == RoleType.staticText)) | 125 lNode.role == RoleType.STATIC_TEXT)) |
126 lNode = lNode.parent; | 126 lNode = lNode.parent; |
127 while (rNode && (rNode.role == RoleType.inlineTextBox || | 127 while (rNode && (rNode.role == RoleType.INLINE_TEXT_BOX || |
128 rNode.role == RoleType.staticText)) | 128 rNode.role == RoleType.STATIC_TEXT)) |
129 rNode = rNode.parent; | 129 rNode = rNode.parent; |
130 | 130 |
131 // Ignore indicies for now. | 131 // Ignore indicies for now. |
132 | 132 |
133 return lNode === rNode && lNode != undefined; | 133 return lNode === rNode && lNode != undefined; |
134 }, | 134 }, |
135 | 135 |
136 /** | 136 /** |
137 * Returns the node. If the node is invalid since the last time it | 137 * Returns the node. If the node is invalid since the last time it |
138 * was accessed, moves the cursor to the nearest valid ancestor first. | 138 * was accessed, moves the cursor to the nearest valid ancestor first. |
(...skipping 30 matching lines...) Expand all Loading... |
169 if (!adjustedNode) | 169 if (!adjustedNode) |
170 return null; | 170 return null; |
171 | 171 |
172 // Make no adjustments if we're within editable content. | 172 // Make no adjustments if we're within editable content. |
173 if (adjustedNode.state.editable) | 173 if (adjustedNode.state.editable) |
174 return adjustedNode; | 174 return adjustedNode; |
175 | 175 |
176 // Selections over line break nodes are broken. | 176 // Selections over line break nodes are broken. |
177 var parent = adjustedNode.parent; | 177 var parent = adjustedNode.parent; |
178 var grandparent = parent && parent.parent; | 178 var grandparent = parent && parent.parent; |
179 if (parent.role == RoleType.lineBreak) { | 179 if (parent.role == RoleType.LINE_BREAK) { |
180 adjustedNode = grandparent; | 180 adjustedNode = grandparent; |
181 } else if (grandparent.role == RoleType.lineBreak) { | 181 } else if (grandparent.role == RoleType.LINE_BREAK) { |
182 adjustedNode = grandparent.parent; | 182 adjustedNode = grandparent.parent; |
183 } else if (this.index_ == cursors.NODE_INDEX || | 183 } else if (this.index_ == cursors.NODE_INDEX || |
184 adjustedNode.role == RoleType.inlineTextBox || | 184 adjustedNode.role == RoleType.INLINE_TEXT_BOX || |
185 chrome.automation.NameFromType[adjustedNode.nameFrom] != 'contents') { | 185 adjustedNode.nameFrom != chrome.automation.NameFromType.CONTENTS) { |
186 // A node offset or unselectable character offset. | 186 // A node offset or unselectable character offset. |
187 adjustedNode = parent; | 187 adjustedNode = parent; |
188 } else { | 188 } else { |
189 // A character offset into content. | 189 // A character offset into content. |
190 adjustedNode = | 190 adjustedNode = |
191 adjustedNode.find({role: RoleType.staticText}) || adjustedNode; | 191 adjustedNode.find({role: RoleType.STATIC_TEXT}) || adjustedNode; |
192 } | 192 } |
193 | 193 |
194 return adjustedNode; | 194 return adjustedNode || null; |
195 }, | 195 }, |
196 | 196 |
197 /** | 197 /** |
198 * An index appropriate for making selections. If this cursor has a | 198 * An index appropriate for making selections. If this cursor has a |
199 * cursors.NODE_INDEX index, the selection index is a node offset e.g. the | 199 * cursors.NODE_INDEX index, the selection index is a node offset e.g. the |
200 * index in parent. If not, the index is a character offset. | 200 * index in parent. If not, the index is a character offset. |
201 * @return {number} | 201 * @return {number} |
202 * @private | 202 * @private |
203 */ | 203 */ |
204 get selectionIndex_() { | 204 get selectionIndex_() { |
205 var adjustedIndex = this.index_; | 205 var adjustedIndex = this.index_; |
206 | 206 |
207 if (!this.node) | 207 if (!this.node) |
208 return -1; | 208 return -1; |
209 | 209 |
210 if (this.node.state.editable) { | 210 if (this.node.state.editable) { |
211 return this.index_ == cursors.NODE_INDEX ? 0 : this.index_; | 211 return this.index_ == cursors.NODE_INDEX ? 0 : this.index_; |
212 } else if (this.node.role == RoleType.inlineTextBox && | 212 } else if (this.node.role == RoleType.INLINE_TEXT_BOX && |
213 // Selections under a line break are broken. | 213 // Selections under a line break are broken. |
214 this.node.parent && this.node.parent.role != RoleType.lineBreak) { | 214 this.node.parent && this.node.parent.role != RoleType.LINE_BREAK) { |
215 if (adjustedIndex == cursors.NODE_INDEX) | 215 if (adjustedIndex == cursors.NODE_INDEX) |
216 adjustedIndex = 0; | 216 adjustedIndex = 0; |
217 | 217 |
218 var sibling = this.node.previousSibling; | 218 var sibling = this.node.previousSibling; |
219 while (sibling) { | 219 while (sibling) { |
220 adjustedIndex += sibling.name.length; | 220 adjustedIndex += sibling.name.length; |
221 sibling = sibling.previousSibling; | 221 sibling = sibling.previousSibling; |
222 } | 222 } |
223 } else if (this.index_ == cursors.NODE_INDEX || | 223 } else if (this.index_ == cursors.NODE_INDEX || |
224 chrome.automation.NameFromType[this.node.nameFrom] != 'contents') { | 224 this.node.nameFrom != chrome.automation.NameFromType.CONTENTS) { |
225 // A node offset or unselectable character offset. | 225 // A node offset or unselectable character offset. |
226 | 226 |
227 // The selected node could have been adjusted upwards in the tree. | 227 // The selected node could have been adjusted upwards in the tree. |
228 var childOfSelection = this.node; | 228 var childOfSelection = this.node; |
229 do { | 229 do { |
230 adjustedIndex = childOfSelection.indexInParent; | 230 adjustedIndex = childOfSelection.indexInParent || 0; |
231 childOfSelection = childOfSelection.parent; | 231 childOfSelection = childOfSelection.parent; |
232 } while (childOfSelection && childOfSelection != this.selectionNode_); | 232 } while (childOfSelection && childOfSelection != this.selectionNode_); |
233 } | 233 } |
234 // A character offset into content is the remaining case. It requires no | 234 // A character offset into content is the remaining case. It requires no |
235 // adjustment. | 235 // adjustment. |
236 | 236 |
237 return adjustedIndex; | 237 return adjustedIndex; |
238 }, | 238 }, |
239 | 239 |
240 /** | 240 /** |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
279 newIndex = | 279 newIndex = |
280 dir == Dir.FORWARD ? 0 : | 280 dir == Dir.FORWARD ? 0 : |
281 StringUtil.previousCodePointOffset(newText, newText.length); | 281 StringUtil.previousCodePointOffset(newText, newText.length); |
282 newIndex = Math.max(newIndex, 0); | 282 newIndex = Math.max(newIndex, 0); |
283 } else { | 283 } else { |
284 newIndex = this.index_; | 284 newIndex = this.index_; |
285 } | 285 } |
286 } | 286 } |
287 break; | 287 break; |
288 case Unit.WORD: | 288 case Unit.WORD: |
289 if (newNode.role != RoleType.inlineTextBox) { | 289 if (newNode.role != RoleType.INLINE_TEXT_BOX) { |
290 newNode = AutomationUtil.findNextNode(newNode, | 290 newNode = AutomationUtil.findNextNode(newNode, |
291 Dir.FORWARD, | 291 Dir.FORWARD, |
292 AutomationPredicate.inlineTextBox, | 292 AutomationPredicate.inlineTextBox, |
293 {skipInitialSubtree: false}) || newNode; | 293 {skipInitialSubtree: false}) || newNode; |
294 } | 294 } |
295 switch (movement) { | 295 switch (movement) { |
296 case Movement.BOUND: | 296 case Movement.BOUND: |
297 if (newNode.role == RoleType.inlineTextBox) { | 297 if (newNode.role == RoleType.INLINE_TEXT_BOX) { |
298 var start, end; | 298 var start, end; |
299 for (var i = 0; i < newNode.wordStarts.length; i++) { | 299 for (var i = 0; i < newNode.wordStarts.length; i++) { |
300 if (newIndex >= newNode.wordStarts[i] && | 300 if (newIndex >= newNode.wordStarts[i] && |
301 newIndex <= newNode.wordEnds[i]) { | 301 newIndex <= newNode.wordEnds[i]) { |
302 start = newNode.wordStarts[i]; | 302 start = newNode.wordStarts[i]; |
303 end = newNode.wordEnds[i]; | 303 end = newNode.wordEnds[i]; |
304 break; | 304 break; |
305 } | 305 } |
306 } | 306 } |
307 if (goog.isDef(start) && goog.isDef(end)) | 307 if (goog.isDef(start) && goog.isDef(end)) |
308 newIndex = dir == Dir.FORWARD ? end : start; | 308 newIndex = dir == Dir.FORWARD ? end : start; |
309 } else { | 309 } else { |
310 // TODO(dtseng): Figure out what to do in this case. | 310 // TODO(dtseng): Figure out what to do in this case. |
311 } | 311 } |
312 break; | 312 break; |
313 case Movement.DIRECTIONAL: | 313 case Movement.DIRECTIONAL: |
314 if (newNode.role == RoleType.inlineTextBox) { | 314 if (newNode.role == RoleType.INLINE_TEXT_BOX) { |
315 var start, end; | 315 var start, end; |
316 for (var i = 0; i < newNode.wordStarts.length; i++) { | 316 for (var i = 0; i < newNode.wordStarts.length; i++) { |
317 if (newIndex >= newNode.wordStarts[i] && | 317 if (newIndex >= newNode.wordStarts[i] && |
318 newIndex <= newNode.wordEnds[i]) { | 318 newIndex <= newNode.wordEnds[i]) { |
319 var nextIndex = dir == Dir.FORWARD ? i + 1 : i - 1; | 319 var nextIndex = dir == Dir.FORWARD ? i + 1 : i - 1; |
320 start = newNode.wordStarts[nextIndex]; | 320 start = newNode.wordStarts[nextIndex]; |
321 end = newNode.wordEnds[nextIndex]; | 321 end = newNode.wordEnds[nextIndex]; |
322 break; | 322 break; |
323 } | 323 } |
324 } | 324 } |
325 if (goog.isDef(start)) { | 325 if (goog.isDef(start)) { |
326 newIndex = start; | 326 newIndex = start; |
327 } else { | 327 } else { |
328 // The backward case is special at the beginning of nodes. | 328 // The backward case is special at the beginning of nodes. |
329 if (dir == Dir.BACKWARD && newIndex != 0) { | 329 if (dir == Dir.BACKWARD && newIndex != 0) { |
330 newIndex = 0; | 330 newIndex = 0; |
331 } else { | 331 } else { |
332 newNode = AutomationUtil.findNextNode(newNode, dir, | 332 newNode = AutomationUtil.findNextNode(newNode, dir, |
333 AutomationPredicate.leaf); | 333 AutomationPredicate.leaf); |
334 if (newNode) { | 334 if (newNode) { |
335 newIndex = 0; | 335 newIndex = 0; |
336 if (dir == Dir.BACKWARD && | 336 if (dir == Dir.BACKWARD && |
337 newNode.role == RoleType.inlineTextBox) { | 337 newNode.role == RoleType.INLINE_TEXT_BOX) { |
338 var starts = newNode.wordStarts; | 338 var starts = newNode.wordStarts; |
339 newIndex = starts[starts.length - 1] || 0; | 339 newIndex = starts[starts.length - 1] || 0; |
340 } else { | 340 } else { |
341 // TODO(dtseng): Figure out what to do for general nodes. | 341 // TODO(dtseng): Figure out what to do for general nodes. |
342 } | 342 } |
343 } | 343 } |
344 } | 344 } |
345 } | 345 } |
346 } else { | 346 } else { |
347 // TODO(dtseng): Figure out what to do in this case. | 347 // TODO(dtseng): Figure out what to do in this case. |
(...skipping 250 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
598 | 598 |
599 /** | 599 /** |
600 * Returns true if this range covers inline text (i.e. each end points to an | 600 * Returns true if this range covers inline text (i.e. each end points to an |
601 * inlineTextBox). | 601 * inlineTextBox). |
602 * @return {boolean?} | 602 * @return {boolean?} |
603 */ | 603 */ |
604 isInlineText: function() { | 604 isInlineText: function() { |
605 return this.start.node && | 605 return this.start.node && |
606 this.end.node && | 606 this.end.node && |
607 this.start.node.role == this.end.node.role && | 607 this.start.node.role == this.end.node.role && |
608 this.start.node.role == RoleType.inlineTextBox; | 608 this.start.node.role == RoleType.INLINE_TEXT_BOX; |
609 }, | 609 }, |
610 | 610 |
611 /** | 611 /** |
612 * Makes a Range which has been moved from this range by the given unit and | 612 * Makes a Range which has been moved from this range by the given unit and |
613 * direction. | 613 * direction. |
614 * @param {Unit} unit | 614 * @param {Unit} unit |
615 * @param {Dir} dir | 615 * @param {Dir} dir |
616 * @return {cursors.Range} | 616 * @return {cursors.Range} |
617 */ | 617 */ |
618 move: function(unit, dir) { | 618 move: function(unit, dir) { |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
650 */ | 650 */ |
651 select: function() { | 651 select: function() { |
652 var startNode = this.start.selectionNode_; | 652 var startNode = this.start.selectionNode_; |
653 var endNode = this.end.selectionNode_; | 653 var endNode = this.end.selectionNode_; |
654 | 654 |
655 if (!startNode || !endNode) | 655 if (!startNode || !endNode) |
656 return; | 656 return; |
657 | 657 |
658 // Only allow selections within the same web tree. | 658 // Only allow selections within the same web tree. |
659 if (startNode.root && | 659 if (startNode.root && |
660 startNode.root.role == RoleType.rootWebArea && | 660 startNode.root.role == RoleType.ROOT_WEB_AREA && |
661 startNode.root == endNode.root) { | 661 startNode.root == endNode.root) { |
662 // We want to adjust to select the entire node for node offsets; | 662 // We want to adjust to select the entire node for node offsets; |
663 // otherwise, use the plain character offset. | 663 // otherwise, use the plain character offset. |
664 var startIndex = this.start.selectionIndex_; | 664 var startIndex = this.start.selectionIndex_; |
665 var endIndex = this.end.index_ == cursors.NODE_INDEX ? | 665 var endIndex = this.end.index_ == cursors.NODE_INDEX ? |
666 this.end.selectionIndex_ + 1 : this.end.selectionIndex_; | 666 this.end.selectionIndex_ + 1 : this.end.selectionIndex_; |
667 | 667 |
668 chrome.automation.setDocumentSelection( | 668 chrome.automation.setDocumentSelection( |
669 { anchorObject: startNode, | 669 { anchorObject: startNode, |
670 anchorOffset: startIndex, | 670 anchorOffset: startIndex, |
671 focusObject: endNode, | 671 focusObject: endNode, |
672 focusOffset: endIndex } | 672 focusOffset: endIndex } |
673 ); | 673 ); |
674 } | 674 } |
675 }, | 675 }, |
676 | 676 |
677 /** | 677 /** |
678 * Returns true if this range has either cursor end on web content. | 678 * Returns true if this range has either cursor end on web content. |
679 * @return {boolean} | 679 * @return {boolean} |
680 */ | 680 */ |
681 isWebRange: function() { | 681 isWebRange: function() { |
682 return this.isValid() && | 682 return this.isValid() && |
683 (this.start.node.root.role != RoleType.desktop || | 683 (this.start.node.root.role != RoleType.DESKTOP || |
684 this.end.node.root.role != RoleType.desktop); | 684 this.end.node.root.role != RoleType.DESKTOP); |
685 }, | 685 }, |
686 | 686 |
687 /** | 687 /** |
688 * Returns whether this range has valid start and end cursors. | 688 * Returns whether this range has valid start and end cursors. |
689 * @return {boolean} | 689 * @return {boolean} |
690 */ | 690 */ |
691 isValid: function() { | 691 isValid: function() { |
692 return this.start.isValid() && this.end.isValid(); | 692 return this.start.isValid() && this.end.isValid(); |
693 } | 693 } |
694 }; | 694 }; |
695 | 695 |
696 }); // goog.scope | 696 }); // goog.scope |
OLD | NEW |