OLD | NEW |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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_ |
OLD | NEW |