Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(203)

Side by Side Diff: ui/accessibility/ax_position.h

Issue 2806773002: Switched to using |AXPosition| for calculating word and line boundaries on Windows. (Closed)
Patch Set: Fixed unit tests. Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « content/browser/accessibility/browser_accessibility_win_unittest.cc ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 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 #ifndef UI_ACCESSIBILITY_AX_POSITION_H_ 5 #ifndef UI_ACCESSIBILITY_AX_POSITION_H_
6 #define UI_ACCESSIBILITY_AX_POSITION_H_ 6 #define UI_ACCESSIBILITY_AX_POSITION_H_
7 7
8 #include <stdint.h> 8 #include <stdint.h>
9 9
10 #include <algorithm> 10 #include <algorithm>
(...skipping 113 matching lines...) Expand 10 before | Expand all | Expand 10 after
124 if (child_index_ == BEFORE_TEXT) { 124 if (child_index_ == BEFORE_TEXT) {
125 str_child_index = "before_text"; 125 str_child_index = "before_text";
126 } else if (child_index_ == INVALID_INDEX) { 126 } else if (child_index_ == INVALID_INDEX) {
127 str_child_index = "invalid"; 127 str_child_index = "invalid";
128 } else { 128 } else {
129 str_child_index = base::IntToString(child_index_); 129 str_child_index = base::IntToString(child_index_);
130 } 130 }
131 str = "TreePosition tree_id=" + base::IntToString(tree_id_) + 131 str = "TreePosition tree_id=" + base::IntToString(tree_id_) +
132 " anchor_id=" + base::IntToString(anchor_id_) + " child_index=" + 132 " anchor_id=" + base::IntToString(anchor_id_) + " child_index=" +
133 str_child_index; 133 str_child_index;
134 break;
134 } 135 }
135 case AXPositionKind::TEXT_POSITION: { 136 case AXPositionKind::TEXT_POSITION: {
136 std::string str_text_offset; 137 std::string str_text_offset;
137 if (text_offset_ == INVALID_OFFSET) { 138 if (text_offset_ == INVALID_OFFSET) {
138 str_text_offset = "invalid"; 139 str_text_offset = "invalid";
139 } else { 140 } else {
140 str_text_offset = base::IntToString(text_offset_); 141 str_text_offset = base::IntToString(text_offset_);
141 } 142 }
142 str = "TextPosition tree_id=" + base::IntToString(tree_id_) + 143 str = "TextPosition tree_id=" + base::IntToString(tree_id_) +
143 " anchor_id=" + base::IntToString(anchor_id_) + " text_offset=" + 144 " anchor_id=" + base::IntToString(anchor_id_) + " text_offset=" +
144 str_text_offset + " affinity=" + 145 str_text_offset + " affinity=" +
145 ui::ToString(static_cast<AXTextAffinity>(affinity_)); 146 ui::ToString(static_cast<AXTextAffinity>(affinity_));
147 break;
146 } 148 }
147 } 149 }
148 150
149 if (!IsTextPosition() || text_offset_ > MaxTextOffset()) 151 if (!IsTextPosition() || text_offset_ > MaxTextOffset())
150 return str; 152 return str;
151 153
152 std::string text = base::UTF16ToUTF8(GetInnerText()); 154 std::string text = base::UTF16ToUTF8(GetInnerText());
153 DCHECK_GE(text_offset_, 0); 155 DCHECK_GE(text_offset_, 0);
154 DCHECK_LE(text_offset_, static_cast<int>(text.length())); 156 DCHECK_LE(text_offset_, static_cast<int>(text.length()));
155 std::string annotated_text; 157 std::string annotated_text;
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after
218 return false; 220 return false;
219 case AXPositionKind::TREE_POSITION: 221 case AXPositionKind::TREE_POSITION:
220 return child_index_ == AnchorChildCount(); 222 return child_index_ == AnchorChildCount();
221 case AXPositionKind::TEXT_POSITION: 223 case AXPositionKind::TEXT_POSITION:
222 return text_offset_ == MaxTextOffset(); 224 return text_offset_ == MaxTextOffset();
223 } 225 }
224 226
225 return false; 227 return false;
226 } 228 }
227 229
230 bool AtStartOfWord() const {
231 AXPositionInstance text_position = AsLeafTextPosition();
232 switch (text_position->kind_) {
233 case AXPositionKind::NULL_POSITION:
234 return false;
235 case AXPositionKind::TREE_POSITION:
236 NOTREACHED();
237 return false;
238 case AXPositionKind::TEXT_POSITION: {
239 const std::vector<int32_t> word_starts =
240 text_position->GetWordStartOffsets();
241 auto iterator =
242 std::find(word_starts.begin(), word_starts.end(),
243 static_cast<int32_t>(text_position->text_offset_));
244 return iterator != word_starts.end();
245 }
246 }
247 return false;
248 }
249
250 bool AtEndOfWord() const {
251 AXPositionInstance text_position = AsLeafTextPosition();
252 switch (text_position->kind_) {
253 case AXPositionKind::NULL_POSITION:
254 return false;
255 case AXPositionKind::TREE_POSITION:
256 NOTREACHED();
257 return false;
258 case AXPositionKind::TEXT_POSITION: {
259 const std::vector<int32_t> word_ends =
260 text_position->GetWordEndOffsets();
261 auto iterator =
262 std::find(word_ends.begin(), word_ends.end(),
263 static_cast<int32_t>(text_position->text_offset_));
264 return iterator != word_ends.end();
265 }
266 }
267 return false;
268 }
269
270 bool AtStartOfLine() const {
271 AXPositionInstance text_position = AsLeafTextPosition();
272 switch (text_position->kind_) {
273 case AXPositionKind::NULL_POSITION:
274 return false;
275 case AXPositionKind::TREE_POSITION:
276 NOTREACHED();
277 return false;
278 case AXPositionKind::TEXT_POSITION:
279 return !text_position->IsInLineBreak() &&
280 GetPreviousOnLineID(text_position->anchor_id_) ==
281 INVALID_ANCHOR_ID &&
282 text_position->AtStartOfAnchor();
283 }
284 return false;
285 }
286
287 bool AtEndOfLine() const {
288 AXPositionInstance text_position = AsLeafTextPosition();
289 switch (text_position->kind_) {
290 case AXPositionKind::NULL_POSITION:
291 return false;
292 case AXPositionKind::TREE_POSITION:
293 NOTREACHED();
294 return false;
295 case AXPositionKind::TEXT_POSITION:
296 return !text_position->IsInLineBreak() &&
297 GetNextOnLineID(text_position->anchor_id_) ==
298 INVALID_ANCHOR_ID &&
299 text_position->AtEndOfAnchor();
300 }
301 return false;
302 }
303
228 // This method returns a position instead of a node because this allows us to 304 // This method returns a position instead of a node because this allows us to
229 // return the corresponding text offset or child index in the ancestor that 305 // return the corresponding text offset or child index in the ancestor that
230 // relates to the current position. 306 // relates to the current position.
231 // Also, this method uses position instead of tree logic to traverse the tree, 307 // Also, this method uses position instead of tree logic to traverse the tree,
232 // because positions can handle moving across multiple trees, while trees 308 // because positions can handle moving across multiple trees, while trees
233 // cannot. 309 // cannot.
234 AXPositionInstance LowestCommonAncestor( 310 AXPositionInstance LowestCommonAncestor(
235 const AXPosition<AXPositionType, AXNodeType>& second) const { 311 const AXPosition<AXPositionType, AXNodeType>& second) const {
236 if (IsNullPosition() || second.IsNullPosition()) 312 if (IsNullPosition() || second.IsNullPosition())
237 return CreateNullPosition(); 313 return CreateNullPosition();
(...skipping 204 matching lines...) Expand 10 before | Expand all | Expand 10 after
442 AXPositionInstance CreateParentPosition() const { 518 AXPositionInstance CreateParentPosition() const {
443 if (IsNullPosition()) 519 if (IsNullPosition())
444 return CreateNullPosition(); 520 return CreateNullPosition();
445 521
446 int tree_id = INVALID_TREE_ID; 522 int tree_id = INVALID_TREE_ID;
447 int32_t parent_id = INVALID_ANCHOR_ID; 523 int32_t parent_id = INVALID_ANCHOR_ID;
448 AnchorParent(&tree_id, &parent_id); 524 AnchorParent(&tree_id, &parent_id);
449 if (tree_id == INVALID_TREE_ID || parent_id == INVALID_ANCHOR_ID) 525 if (tree_id == INVALID_TREE_ID || parent_id == INVALID_ANCHOR_ID)
450 return CreateNullPosition(); 526 return CreateNullPosition();
451 527
452 DCHECK_NE(tree_id, INVALID_TREE_ID);
453 DCHECK_NE(parent_id, INVALID_ANCHOR_ID);
454 switch (kind_) { 528 switch (kind_) {
455 case AXPositionKind::NULL_POSITION: 529 case AXPositionKind::NULL_POSITION:
456 NOTREACHED(); 530 NOTREACHED();
457 return CreateNullPosition(); 531 return CreateNullPosition();
458 case AXPositionKind::TREE_POSITION: 532 case AXPositionKind::TREE_POSITION:
459 return CreateTreePosition(tree_id, parent_id, AnchorIndexInParent()); 533 return CreateTreePosition(tree_id, parent_id, AnchorIndexInParent());
460 case AXPositionKind::TEXT_POSITION: 534 case AXPositionKind::TEXT_POSITION: {
461 // Make sure that our affinity is propagated to our parent because by 535 // If our parent contains all our text, we need to maintain the affinity
462 // design our parent includes all our text. 536 // and the text offset. Otherwise, we return a position that is either
463 return CreateTextPosition(tree_id, parent_id, 537 // before or after the child and we don't maintain the affinity when the
464 AnchorTextOffsetInParent() + text_offset_, 538 // position is after the child.
465 affinity_); 539 int parent_offset = AnchorTextOffsetInParent();
540 AXTextAffinity parent_affinity = affinity_;
541 if (MaxTextOffset() == MaxTextOffsetInParent()) {
542 parent_offset += text_offset_;
543 } else if (text_offset_ > 0) {
544 parent_offset += MaxTextOffsetInParent();
545 parent_affinity = AX_TEXT_AFFINITY_DOWNSTREAM;
546 }
547 return CreateTextPosition(tree_id, parent_id, parent_offset,
548 parent_affinity);
549 }
466 } 550 }
467 551
468 return CreateNullPosition(); 552 return CreateNullPosition();
469 } 553 }
470 554
471 // Creates a position using the next text-only node as its anchor. 555 // Creates a position using the next text-only node as its anchor.
472 // Assumes that text-only nodes are leaf nodes. 556 // Assumes that text-only nodes are leaf nodes.
473 AXPositionInstance CreateNextTextAnchorPosition() const { 557 AXPositionInstance CreateNextTextAnchorPosition() const {
474 AXPositionInstance next_leaf(CreateNextAnchorPosition()); 558 AXPositionInstance next_leaf(CreateNextAnchorPosition());
475 while (!next_leaf->IsNullPosition() && next_leaf->AnchorChildCount()) { 559 while (!next_leaf->IsNullPosition() && next_leaf->AnchorChildCount()) {
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after
541 text_position = text_position->AsTreePosition(); 625 text_position = text_position->AsTreePosition();
542 return text_position; 626 return text_position;
543 } 627 }
544 628
545 AXPositionInstance CreateNextWordStartPosition() const { 629 AXPositionInstance CreateNextWordStartPosition() const {
546 bool was_tree_position = IsTreePosition(); 630 bool was_tree_position = IsTreePosition();
547 AXPositionInstance text_position = AsLeafTextPosition(); 631 AXPositionInstance text_position = AsLeafTextPosition();
548 if (text_position->IsNullPosition()) 632 if (text_position->IsNullPosition())
549 return text_position; 633 return text_position;
550 634
551 // Ignore any nodes with no text or no word boundaries.
552 while (!text_position->IsNullPosition() &&
553 (!text_position->MaxTextOffset() ||
554 text_position->GetWordStartOffsets().empty())) {
555 text_position = text_position->CreateNextTextAnchorPosition();
556 }
557
558 if (text_position->IsNullPosition())
559 return text_position;
560
561 const std::vector<int32_t> word_starts = 635 const std::vector<int32_t> word_starts =
562 text_position->GetWordStartOffsets(); 636 text_position->GetWordStartOffsets();
563 auto iterator = 637 auto iterator =
564 std::upper_bound(word_starts.begin(), word_starts.end(), 638 std::upper_bound(word_starts.begin(), word_starts.end(),
565 static_cast<int32_t>(text_position->text_offset_)); 639 static_cast<int32_t>(text_position->text_offset_));
566 if (iterator == word_starts.end()) { 640 if (iterator == word_starts.end()) {
641 // Ignore any nodes with no text or no word boundaries.
567 do { 642 do {
568 text_position = text_position->CreateNextTextAnchorPosition(); 643 text_position = text_position->CreateNextTextAnchorPosition();
569 } while (!text_position->IsNullPosition() && 644 } while (!text_position->IsNullPosition() &&
570 (!text_position->MaxTextOffset() || 645 (!text_position->MaxTextOffset() ||
571 text_position->GetWordStartOffsets().empty())); 646 text_position->GetWordStartOffsets().empty()));
572 if (text_position->IsNullPosition()) 647 if (text_position->IsNullPosition())
573 return text_position; 648 return text_position;
574 649
575 // In case there are some non-word characters in front of the first word
576 // in this text node.
577 const std::vector<int32_t> word_starts = 650 const std::vector<int32_t> word_starts =
578 text_position->GetWordStartOffsets(); 651 text_position->GetWordStartOffsets();
652 DCHECK(!word_starts.empty());
579 text_position->text_offset_ = static_cast<int>(word_starts[0]); 653 text_position->text_offset_ = static_cast<int>(word_starts[0]);
580 } else { 654 } else {
581 text_position->text_offset_ = static_cast<int>(*iterator); 655 text_position->text_offset_ = static_cast<int>(*iterator);
582 text_position->affinity_ = AX_TEXT_AFFINITY_DOWNSTREAM; 656 text_position->affinity_ = AX_TEXT_AFFINITY_DOWNSTREAM;
583 } 657 }
584 658
585 // If the word boundary is in the same subtree, return a position rooted at 659 // If the word boundary is in the same subtree, return a position rooted at
586 // the current position. 660 // the current position.
587 // This is necessary because we don't want to return any position that might 661 // This is necessary because we don't want to return any position that might
588 // be in the shadow DOM if the original position was not. 662 // be in the shadow DOM if the original position was not.
589 AXPositionInstance common_ancestor = 663 AXPositionInstance common_ancestor =
590 text_position->LowestCommonAncestor(*this); 664 text_position->LowestCommonAncestor(*this);
591 if (GetAnchor() == common_ancestor->GetAnchor()) 665 if (GetAnchor() == common_ancestor->GetAnchor())
592 return common_ancestor; 666 text_position = std::move(common_ancestor);
593 667
594 if (was_tree_position) 668 if (was_tree_position)
595 text_position = text_position->AsTreePosition(); 669 text_position = text_position->AsTreePosition();
596 return text_position; 670 return text_position;
597 } 671 }
598 672
599 AXPositionInstance CreatePreviousWordStartPosition() const { 673 AXPositionInstance CreatePreviousWordStartPosition() const {
600 bool was_tree_position = IsTreePosition(); 674 bool was_tree_position = IsTreePosition();
601 AXPositionInstance text_position = AsLeafTextPosition(); 675 AXPositionInstance text_position = AsLeafTextPosition();
602 if (text_position->IsNullPosition())
603 return text_position;
604 676
605 if (text_position->AtStartOfAnchor()) { 677 if (text_position->AtStartOfAnchor()) {
606 text_position = text_position->CreatePreviousTextAnchorPosition(); 678 text_position = text_position->CreatePreviousTextAnchorPosition();
607 text_position = text_position->CreatePositionAtEndOfAnchor(); 679 text_position = text_position->CreatePositionAtEndOfAnchor();
608 } 680 }
609
610 // Ignore any nodes with no text or no word boundaries.
611 while (!text_position->IsNullPosition() &&
612 (!text_position->MaxTextOffset() ||
613 text_position->GetWordStartOffsets().empty())) {
614 text_position = text_position->CreatePreviousTextAnchorPosition();
615 text_position = text_position->CreatePositionAtEndOfAnchor();
616 }
617
618 if (text_position->IsNullPosition()) 681 if (text_position->IsNullPosition())
619 return text_position; 682 return text_position;
620 683
621 const std::vector<int32_t> word_starts = 684 const std::vector<int32_t> word_starts =
622 text_position->GetWordStartOffsets(); 685 text_position->GetWordStartOffsets();
623 auto iterator = 686 auto iterator =
624 std::lower_bound(word_starts.begin(), word_starts.end(), 687 std::lower_bound(word_starts.begin(), word_starts.end(),
625 static_cast<int32_t>(text_position->text_offset_)); 688 static_cast<int32_t>(text_position->text_offset_));
626 if (iterator == word_starts.begin()) { 689 if (word_starts.empty() || iterator == word_starts.begin()) {
627 // There must be some non-word characters in front of the first word in 690 // Ignore any nodes with no text or no word boundaries.
628 // this text node.
629 do { 691 do {
630 text_position = text_position->CreatePreviousTextAnchorPosition(); 692 text_position = text_position->CreatePreviousTextAnchorPosition();
631 } while (!text_position->IsNullPosition() && 693 } while (!text_position->IsNullPosition() &&
632 (!text_position->MaxTextOffset() || 694 (!text_position->MaxTextOffset() ||
633 text_position->GetWordStartOffsets().empty())); 695 text_position->GetWordStartOffsets().empty()));
634 if (text_position->IsNullPosition()) 696 if (text_position->IsNullPosition())
635 return text_position; 697 return text_position;
636 698
637 const std::vector<int32_t> word_starts = 699 const std::vector<int32_t> word_starts =
638 text_position->GetWordStartOffsets(); 700 text_position->GetWordStartOffsets();
701 DCHECK(!word_starts.empty());
639 text_position->text_offset_ = static_cast<int>(*(word_starts.end() - 1)); 702 text_position->text_offset_ = static_cast<int>(*(word_starts.end() - 1));
640 } else { 703 } else {
641 text_position->text_offset_ = static_cast<int>(*(--iterator)); 704 text_position->text_offset_ = static_cast<int>(*(--iterator));
642 text_position->affinity_ = AX_TEXT_AFFINITY_DOWNSTREAM; 705 text_position->affinity_ = AX_TEXT_AFFINITY_DOWNSTREAM;
643 } 706 }
644 707
645 // If the word boundary is in the same subtree, return a position rooted at 708 // If the word boundary is in the same subtree, return a position rooted at
646 // the current position. 709 // the current position.
647 // This is necessary because we don't want to return any position that might 710 // This is necessary because we don't want to return any position that might
648 // be in the shadow DOM if the original position was not. 711 // be in the shadow DOM if the original position was not.
649 AXPositionInstance common_ancestor = 712 AXPositionInstance common_ancestor =
650 text_position->LowestCommonAncestor(*this); 713 text_position->LowestCommonAncestor(*this);
651 if (GetAnchor() == common_ancestor->GetAnchor()) 714 if (GetAnchor() == common_ancestor->GetAnchor())
652 return common_ancestor; 715 text_position = std::move(common_ancestor);
653 716
654 if (was_tree_position) 717 if (was_tree_position)
655 text_position = text_position->AsTreePosition(); 718 text_position = text_position->AsTreePosition();
656 return text_position; 719 return text_position;
657 } 720 }
658 721
659 // Word end positions are one past the last character of the word. 722 // Word end positions are one past the last character of the word.
660 AXPositionInstance CreateNextWordEndPosition() const { 723 AXPositionInstance CreateNextWordEndPosition() const {
661 bool was_tree_position = IsTreePosition(); 724 bool was_tree_position = IsTreePosition();
662 AXPositionInstance text_position = AsLeafTextPosition(); 725 AXPositionInstance text_position = AsLeafTextPosition();
726
727 if (text_position->AtEndOfAnchor())
728 text_position = text_position->CreateNextTextAnchorPosition();
663 if (text_position->IsNullPosition()) 729 if (text_position->IsNullPosition())
664 return text_position; 730 return text_position;
665 731
666 if (text_position->AtEndOfAnchor()) 732 const std::vector<int32_t> word_ends = text_position->GetWordEndOffsets();
667 text_position = text_position->CreateNextTextAnchorPosition(); 733 auto iterator =
668 734 std::upper_bound(word_ends.begin(), word_ends.end(),
669 // Ignore any nodes with no text or no word boundaries. 735 static_cast<int32_t>(text_position->text_offset_));
670 while (!text_position->IsNullPosition() &&
671 (!text_position->MaxTextOffset() ||
672 text_position->GetWordEndOffsets().empty())) {
673 text_position = text_position->CreateNextTextAnchorPosition();
674 }
675
676 if (text_position->IsNullPosition())
677 return text_position;
678
679 const std::vector<int> word_ends = text_position->GetWordEndOffsets();
680 auto iterator = std::upper_bound(word_ends.begin(), word_ends.end(),
681 text_position->text_offset_);
682 if (iterator == word_ends.end()) { 736 if (iterator == word_ends.end()) {
683 // We should be in the last word of this text node. 737 // Ignore any nodes with no text or no word boundaries.
684 do { 738 do {
685 text_position = text_position->CreateNextTextAnchorPosition(); 739 text_position = text_position->CreateNextTextAnchorPosition();
686 } while (!text_position->IsNullPosition() && 740 } while (!text_position->IsNullPosition() &&
687 (!text_position->MaxTextOffset() || 741 (!text_position->MaxTextOffset() ||
688 text_position->GetWordEndOffsets().empty())); 742 text_position->GetWordEndOffsets().empty()));
689 if (text_position->IsNullPosition()) 743 if (text_position->IsNullPosition())
690 return text_position; 744 return text_position;
691 745
692 const std::vector<int> word_ends = text_position->GetWordEndOffsets(); 746 const std::vector<int32_t> word_ends = text_position->GetWordEndOffsets();
693 text_position->text_offset_ = word_ends[0]; 747 DCHECK(!word_ends.empty());
748 text_position->text_offset_ = static_cast<int>(word_ends[0]);
694 } else { 749 } else {
695 text_position->text_offset_ = *iterator; 750 text_position->text_offset_ = static_cast<int>(*iterator);
696 text_position->affinity_ = AX_TEXT_AFFINITY_DOWNSTREAM; 751 text_position->affinity_ = AX_TEXT_AFFINITY_DOWNSTREAM;
697 } 752 }
698 753
699 // If the word boundary is in the same subtree, return a position rooted at 754 // If the word boundary is in the same subtree, return a position rooted at
700 // the current position. 755 // the current position.
701 // This is necessary because we don't want to return any position that might 756 // This is necessary because we don't want to return any position that might
702 // be in the shadow DOM if the original position was not. 757 // be in the shadow DOM if the original position was not.
703 AXPositionInstance common_ancestor = 758 AXPositionInstance common_ancestor =
704 text_position->LowestCommonAncestor(*this); 759 text_position->LowestCommonAncestor(*this);
705 if (GetAnchor() == common_ancestor->GetAnchor()) 760 if (GetAnchor() == common_ancestor->GetAnchor())
706 return common_ancestor; 761 text_position = std::move(common_ancestor);
707 762
708 if (was_tree_position) 763 if (was_tree_position)
709 text_position = text_position->AsTreePosition(); 764 text_position = text_position->AsTreePosition();
710 return text_position; 765 return text_position;
711 } 766 }
712 767
713 // Word end positions are one past the last character of the word. 768 // Word end positions are one past the last character of the word.
714 AXPositionInstance CreatePreviousWordEndPosition() const { 769 AXPositionInstance CreatePreviousWordEndPosition() const {
715 bool was_tree_position = IsTreePosition(); 770 bool was_tree_position = IsTreePosition();
716 AXPositionInstance text_position = AsLeafTextPosition(); 771 AXPositionInstance text_position = AsLeafTextPosition();
717 if (text_position->IsNullPosition())
718 return text_position;
719 772
720 if (text_position->AtStartOfAnchor()) { 773 if (text_position->AtStartOfAnchor()) {
721 text_position = text_position->CreatePreviousTextAnchorPosition(); 774 text_position = text_position->CreatePreviousTextAnchorPosition();
722 text_position = text_position->CreatePositionAtEndOfAnchor(); 775 text_position = text_position->CreatePositionAtEndOfAnchor();
723 } 776 }
724
725 // Ignore any nodes with no text or no word boundaries.
726 while (!text_position->IsNullPosition() &&
727 (!text_position->MaxTextOffset() ||
728 text_position->GetWordStartOffsets().empty())) {
729 text_position = text_position->CreatePreviousTextAnchorPosition();
730 }
731
732 if (text_position->IsNullPosition()) 777 if (text_position->IsNullPosition())
733 return text_position; 778 return text_position;
734 779
735 const std::vector<int32_t> word_ends = text_position->GetWordEndOffsets(); 780 const std::vector<int32_t> word_ends = text_position->GetWordEndOffsets();
736 auto iterator = 781 auto iterator =
737 std::lower_bound(word_ends.begin(), word_ends.end(), 782 std::lower_bound(word_ends.begin(), word_ends.end(),
738 static_cast<int32_t>(text_position->text_offset_)); 783 static_cast<int32_t>(text_position->text_offset_));
739 if (iterator == word_ends.begin()) { 784 if (word_ends.empty() || iterator == word_ends.begin()) {
740 // We must be anywhere up to and including the end of the first word in 785 // Ignore any nodes with no text or no word boundaries.
741 // this node.
742 do { 786 do {
743 text_position = text_position->CreatePreviousTextAnchorPosition(); 787 text_position = text_position->CreatePreviousTextAnchorPosition();
744 } while (!text_position->IsNullPosition() && 788 } while (!text_position->IsNullPosition() &&
745 (!text_position->MaxTextOffset() || 789 (!text_position->MaxTextOffset() ||
746 text_position->GetWordStartOffsets().empty())); 790 text_position->GetWordStartOffsets().empty()));
747 if (text_position->IsNullPosition()) 791 if (text_position->IsNullPosition())
748 return text_position; 792 return text_position;
749 793
750 const std::vector<int32_t> word_ends = text_position->GetWordEndOffsets(); 794 const std::vector<int32_t> word_ends = text_position->GetWordEndOffsets();
795 DCHECK(!word_ends.empty());
751 text_position->text_offset_ = static_cast<int>(*(word_ends.end() - 1)); 796 text_position->text_offset_ = static_cast<int>(*(word_ends.end() - 1));
752 } else { 797 } else {
753 text_position->text_offset_ = static_cast<int>(*(--iterator)); 798 text_position->text_offset_ = static_cast<int>(*(--iterator));
754 text_position->affinity_ = AX_TEXT_AFFINITY_DOWNSTREAM; 799 text_position->affinity_ = AX_TEXT_AFFINITY_DOWNSTREAM;
755 } 800 }
756 801
757 // If the word boundary is in the same subtree, return a position rooted at 802 // If the word boundary is in the same subtree, return a position rooted at
758 // the current position. 803 // the current position.
759 // This is necessary because we don't want to return any position that might 804 // This is necessary because we don't want to return any position that might
760 // be in the shadow DOM if the original position was not. 805 // be in the shadow DOM if the original position was not.
761 AXPositionInstance common_ancestor = 806 AXPositionInstance common_ancestor =
762 text_position->LowestCommonAncestor(*this); 807 text_position->LowestCommonAncestor(*this);
763 if (GetAnchor() == common_ancestor->GetAnchor()) 808 if (GetAnchor() == common_ancestor->GetAnchor())
764 return common_ancestor; 809 text_position = std::move(common_ancestor);
765 810
766 if (was_tree_position) 811 if (was_tree_position)
767 text_position = text_position->AsTreePosition(); 812 text_position = text_position->AsTreePosition();
768 return text_position; 813 return text_position;
769 } 814 }
770 815
771 AXPositionInstance CreateNextLineStartPosition() const { 816 AXPositionInstance CreateNextLineStartPosition() const {
772 bool was_tree_position = IsTreePosition(); 817 bool was_tree_position = IsTreePosition();
773 AXPositionInstance text_position = AsLeafTextPosition(); 818 AXPositionInstance text_position = AsLeafTextPosition();
774 if (text_position->IsNullPosition()) 819 if (text_position->IsNullPosition())
775 return text_position; 820 return text_position;
776 821
777 // Find the next line break. 822 // Find the next line break.
778 int32_t next_on_line_id = text_position->anchor_id_; 823 int32_t next_on_line_id = text_position->anchor_id_;
779 while (GetNextOnLineID(next_on_line_id) != INVALID_ANCHOR_ID) 824 while (GetNextOnLineID(next_on_line_id) != INVALID_ANCHOR_ID)
780 next_on_line_id = GetNextOnLineID(next_on_line_id); 825 next_on_line_id = GetNextOnLineID(next_on_line_id);
781 text_position = 826 text_position =
782 CreateTextPosition(tree_id_, next_on_line_id, 0 /* text_offset */, 827 CreateTextPosition(tree_id_, next_on_line_id, 0 /* text_offset */,
783 AX_TEXT_AFFINITY_DOWNSTREAM); 828 AX_TEXT_AFFINITY_DOWNSTREAM);
784 text_position = 829 text_position =
785 text_position->AsLeafTextPosition()->CreateNextTextAnchorPosition(); 830 text_position->AsLeafTextPosition()->CreateNextTextAnchorPosition();
831 while (text_position->IsInLineBreak())
832 text_position = text_position->CreateNextTextAnchorPosition();
786 if (text_position->IsNullPosition()) 833 if (text_position->IsNullPosition())
787 return text_position; 834 return text_position;
788 835
789 // If the line boundary is in the same subtree, return a position rooted at 836 // If the line boundary is in the same subtree, return a position rooted at
790 // the current position. 837 // the current position.
791 // This is necessary because we don't want to return any position that might 838 // This is necessary because we don't want to return any position that might
792 // be in the shadow DOM if the original position was not. 839 // be in the shadow DOM if the original position was not.
793 AXPositionInstance common_ancestor = 840 AXPositionInstance common_ancestor =
794 text_position->LowestCommonAncestor(*this); 841 text_position->LowestCommonAncestor(*this);
795 if (GetAnchor() == common_ancestor->GetAnchor()) 842 if (GetAnchor() == common_ancestor->GetAnchor())
796 return common_ancestor; 843 text_position = std::move(common_ancestor);
797 844
798 if (was_tree_position) 845 if (was_tree_position)
799 text_position = text_position->AsTreePosition(); 846 text_position = text_position->AsTreePosition();
800 return text_position; 847 return text_position;
801 } 848 }
802 849
803 AXPositionInstance CreatePreviousLineStartPosition() const { 850 AXPositionInstance CreatePreviousLineStartPosition() const {
804 bool was_tree_position = IsTreePosition(); 851 bool was_tree_position = IsTreePosition();
805 AXPositionInstance text_position = AsLeafTextPosition(); 852 AXPositionInstance text_position = AsLeafTextPosition();
806 if (text_position->AtStartOfAnchor()) 853 if (text_position->IsInLineBreak() || text_position->AtStartOfAnchor())
807 text_position = text_position->CreatePreviousTextAnchorPosition(); 854 text_position = text_position->CreatePreviousTextAnchorPosition();
808 if (text_position->IsNullPosition()) 855 if (text_position->IsNullPosition())
809 return text_position; 856 return text_position;
810 857
811 int32_t previous_on_line_id = text_position->anchor_id_; 858 int32_t previous_on_line_id = text_position->anchor_id_;
812 while (GetPreviousOnLineID(previous_on_line_id) != INVALID_ANCHOR_ID) 859 while (GetPreviousOnLineID(previous_on_line_id) != INVALID_ANCHOR_ID)
813 previous_on_line_id = GetPreviousOnLineID(previous_on_line_id); 860 previous_on_line_id = GetPreviousOnLineID(previous_on_line_id);
814 text_position = 861 text_position =
815 CreateTextPosition(tree_id_, previous_on_line_id, 0 /* text_offset */, 862 CreateTextPosition(tree_id_, previous_on_line_id, 0 /* text_offset */,
816 AX_TEXT_AFFINITY_DOWNSTREAM); 863 AX_TEXT_AFFINITY_DOWNSTREAM);
817 text_position = text_position->AsLeafTextPosition(); 864 text_position = text_position->AsLeafTextPosition();
818 if (text_position->IsNullPosition()) 865 if (text_position->IsNullPosition())
819 return text_position; 866 return text_position;
820 867
821 // If the line boundary is in the same subtree, return a position rooted at 868 // If the line boundary is in the same subtree, return a position rooted at
822 // the current position. 869 // the current position.
823 // This is necessary because we don't want to return any position that might 870 // This is necessary because we don't want to return any position that might
824 // be in the shadow DOM if the original position was not. 871 // be in the shadow DOM if the original position was not.
825 AXPositionInstance common_ancestor = 872 AXPositionInstance common_ancestor =
826 text_position->LowestCommonAncestor(*this); 873 text_position->LowestCommonAncestor(*this);
827 if (GetAnchor() == common_ancestor->GetAnchor()) 874 if (GetAnchor() == common_ancestor->GetAnchor())
828 return common_ancestor; 875 text_position = std::move(common_ancestor);
829 876
830 if (was_tree_position) 877 if (was_tree_position)
831 text_position = text_position->AsTreePosition(); 878 text_position = text_position->AsTreePosition();
832 return text_position; 879 return text_position;
833 } 880 }
834 881
835 // Line end positions are one past the last character of the line, excluding 882 // Line end positions are one past the last character of the line, excluding
836 // any newline characters. 883 // any newline characters.
837 AXPositionInstance CreateNextLineEndPosition() const { 884 AXPositionInstance CreateNextLineEndPosition() const {
838 bool was_tree_position = IsTreePosition(); 885 bool was_tree_position = IsTreePosition();
839 AXPositionInstance text_position = AsLeafTextPosition(); 886 AXPositionInstance text_position = AsLeafTextPosition();
840 // Skip forward to the next line if we are at the end of one. 887 // Skip forward to the next line if we are at the end of one.
841 // Note that not all lines end with a hard line break. 888 // Note that not all lines end with a hard line break.
842 while (text_position->IsInLineBreak() || text_position->AtEndOfAnchor()) 889 while (text_position->IsInLineBreak() || text_position->AtEndOfAnchor())
843 text_position = text_position->CreateNextTextAnchorPosition(); 890 text_position = text_position->CreateNextTextAnchorPosition();
844 if (text_position->IsNullPosition()) 891 if (text_position->IsNullPosition())
845 return text_position; 892 return text_position;
846 893
847 // Find the next line break. 894 // Find the next line break.
848 int32_t next_on_line_id = text_position->anchor_id_; 895 int32_t next_on_line_id = text_position->anchor_id_;
849 while (GetNextOnLineID(next_on_line_id) != INVALID_ANCHOR_ID) 896 while (GetNextOnLineID(next_on_line_id) != INVALID_ANCHOR_ID)
850 next_on_line_id = GetNextOnLineID(next_on_line_id); 897 next_on_line_id = GetNextOnLineID(next_on_line_id);
851 text_position = 898 text_position =
852 CreateTextPosition(tree_id_, next_on_line_id, 0 /* text_offset */, 899 CreateTextPosition(tree_id_, next_on_line_id, 0 /* text_offset */,
853 AX_TEXT_AFFINITY_DOWNSTREAM); 900 AX_TEXT_AFFINITY_DOWNSTREAM);
854 text_position = text_position->AsLeafTextPosition(); 901 text_position = text_position->AsLeafTextPosition();
855 while (text_position->IsInLineBreak()) { 902 while (text_position->IsInLineBreak())
856 text_position = text_position->CreatePreviousTextAnchorPosition(); 903 text_position = text_position->CreatePreviousTextAnchorPosition();
857 }
858 if (text_position->IsNullPosition()) 904 if (text_position->IsNullPosition())
859 return text_position; 905 return text_position;
860 text_position = text_position->CreatePositionAtEndOfAnchor(); 906 text_position = text_position->CreatePositionAtEndOfAnchor();
861 907
862 // If the line boundary is in the same subtree, return a position rooted at 908 // If the line boundary is in the same subtree, return a position rooted at
863 // the current position. 909 // the current position.
864 // This is necessary because we don't want to return any position that might 910 // This is necessary because we don't want to return any position that might
865 // be in the shadow DOM if the original position was not. 911 // be in the shadow DOM if the original position was not.
866 AXPositionInstance common_ancestor = 912 AXPositionInstance common_ancestor =
867 text_position->LowestCommonAncestor(*this); 913 text_position->LowestCommonAncestor(*this);
868 if (GetAnchor() == common_ancestor->GetAnchor()) 914 if (GetAnchor() == common_ancestor->GetAnchor())
869 return common_ancestor; 915 text_position = std::move(common_ancestor);
870 916
871 if (was_tree_position) 917 if (was_tree_position)
872 text_position = text_position->AsTreePosition(); 918 text_position = text_position->AsTreePosition();
873 return text_position; 919 return text_position;
874 } 920 }
875 921
876 // Line end positions are one past the last character of the line, excluding 922 // Line end positions are one past the last character of the line, excluding
877 // any newline characters. 923 // any newline characters.
878 AXPositionInstance CreatePreviousLineEndPosition() const { 924 AXPositionInstance CreatePreviousLineEndPosition() const {
879 bool was_tree_position = IsTreePosition(); 925 bool was_tree_position = IsTreePosition();
880 AXPositionInstance text_position = AsLeafTextPosition(); 926 AXPositionInstance text_position = AsLeafTextPosition();
881 if (text_position->IsNullPosition()) 927 if (text_position->IsNullPosition())
882 return text_position; 928 return text_position;
883 929
884 int32_t previous_on_line_id = text_position->anchor_id_; 930 int32_t previous_on_line_id = text_position->anchor_id_;
885 while (GetPreviousOnLineID(previous_on_line_id) != INVALID_ANCHOR_ID) 931 while (GetPreviousOnLineID(previous_on_line_id) != INVALID_ANCHOR_ID)
886 previous_on_line_id = GetPreviousOnLineID(previous_on_line_id); 932 previous_on_line_id = GetPreviousOnLineID(previous_on_line_id);
887 text_position = 933 text_position =
888 CreateTextPosition(tree_id_, previous_on_line_id, 0 /* text_offset */, 934 CreateTextPosition(tree_id_, previous_on_line_id, 0 /* text_offset */,
889 AX_TEXT_AFFINITY_DOWNSTREAM); 935 AX_TEXT_AFFINITY_DOWNSTREAM);
890 text_position = 936 text_position =
891 text_position->AsLeafTextPosition()->CreatePreviousTextAnchorPosition(); 937 text_position->AsLeafTextPosition()->CreatePreviousTextAnchorPosition();
892 while (text_position->IsInLineBreak()) { 938 while (text_position->IsInLineBreak())
893 text_position = text_position->CreatePreviousTextAnchorPosition(); 939 text_position = text_position->CreatePreviousTextAnchorPosition();
894 }
895 if (text_position->IsNullPosition()) 940 if (text_position->IsNullPosition())
896 return text_position; 941 return text_position;
897 text_position = text_position->CreatePositionAtEndOfAnchor(); 942 text_position = text_position->CreatePositionAtEndOfAnchor();
898 943
899 // If the line boundary is in the same subtree, return a position rooted at 944 // If the line boundary is in the same subtree, return a position rooted at
900 // the current position. 945 // the current position.
901 // This is necessary because we don't want to return any position that might 946 // This is necessary because we don't want to return any position that might
902 // be in the shadow DOM if the original position was not. 947 // be in the shadow DOM if the original position was not.
903 AXPositionInstance common_ancestor = 948 AXPositionInstance common_ancestor =
904 text_position->LowestCommonAncestor(*this); 949 text_position->LowestCommonAncestor(*this);
905 if (GetAnchor() == common_ancestor->GetAnchor()) 950 if (GetAnchor() == common_ancestor->GetAnchor())
906 return common_ancestor; 951 text_position = std::move(common_ancestor);
907 952
908 if (was_tree_position) 953 if (was_tree_position)
909 text_position = text_position->AsTreePosition(); 954 text_position = text_position->AsTreePosition();
910 return text_position; 955 return text_position;
911 } 956 }
912 957
913 // TODO(nektar): Add sentence and paragraph navigation methods. 958 // TODO(nektar): Add sentence and paragraph navigation methods.
914 959
915 // Abstract methods. 960 // Abstract methods.
916 961
(...skipping 224 matching lines...) Expand 10 before | Expand all | Expand 10 after
1141 1186
1142 template <class AXPositionType, class AXNodeType> 1187 template <class AXPositionType, class AXNodeType>
1143 bool operator>=(const AXPosition<AXPositionType, AXNodeType>& first, 1188 bool operator>=(const AXPosition<AXPositionType, AXNodeType>& first,
1144 const AXPosition<AXPositionType, AXNodeType>& second) { 1189 const AXPosition<AXPositionType, AXNodeType>& second) {
1145 return first == second || first > second; 1190 return first == second || first > second;
1146 } 1191 }
1147 1192
1148 } // namespace ui 1193 } // namespace ui
1149 1194
1150 #endif // UI_ACCESSIBILITY_AX_POSITION_H_ 1195 #endif // UI_ACCESSIBILITY_AX_POSITION_H_
OLDNEW
« no previous file with comments | « content/browser/accessibility/browser_accessibility_win_unittest.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698