| 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 |