| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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 #include "content/browser/accessibility/browser_accessibility.h" | 5 #include "content/browser/accessibility/browser_accessibility.h" |
| 6 | 6 |
| 7 #include <stddef.h> | 7 #include <stddef.h> |
| 8 | 8 |
| 9 #include <algorithm> | 9 #include <algorithm> |
| 10 | 10 |
| (...skipping 508 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 519 // Some screen readers like Jaws and older versions of VoiceOver require a | 519 // Some screen readers like Jaws and older versions of VoiceOver require a |
| 520 // value to be set in text fields with rich content, even though the same | 520 // value to be set in text fields with rich content, even though the same |
| 521 // information is available on the children. | 521 // information is available on the children. |
| 522 if (value.empty() && | 522 if (value.empty() && |
| 523 (IsSimpleTextControl() || IsRichTextControl()) && | 523 (IsSimpleTextControl() || IsRichTextControl()) && |
| 524 !IsNativeTextControl()) | 524 !IsNativeTextControl()) |
| 525 value = GetInnerText(); | 525 value = GetInnerText(); |
| 526 return value; | 526 return value; |
| 527 } | 527 } |
| 528 | 528 |
| 529 int BrowserAccessibility::GetLineStartBoundary( | |
| 530 int start, | |
| 531 ui::TextBoundaryDirection direction, | |
| 532 ui::AXTextAffinity affinity) const { | |
| 533 DCHECK_GE(start, 0); | |
| 534 DCHECK_LE(start, static_cast<int>(GetText().length())); | |
| 535 | |
| 536 if (IsSimpleTextControl()) { | |
| 537 return ui::FindAccessibleTextBoundary(GetText(), GetLineStartOffsets(), | |
| 538 ui::LINE_BOUNDARY, start, direction, | |
| 539 affinity); | |
| 540 } | |
| 541 | |
| 542 // Keeps track of the start offset of each consecutive line. | |
| 543 int line_start = 0; | |
| 544 // Keeps track of the length of each consecutive line. | |
| 545 int line_length = 0; | |
| 546 for (size_t i = 0; i < InternalChildCount(); ++i) { | |
| 547 const BrowserAccessibility* child = InternalGetChild(i); | |
| 548 DCHECK(child); | |
| 549 // Child objects are of length one, since they are represented by a | |
| 550 // single embedded object character. The exception is text-only objects. | |
| 551 int child_length = 1; | |
| 552 if (child->IsTextOnlyObject()) | |
| 553 child_length = static_cast<int>(child->GetText().length()); | |
| 554 | |
| 555 // Determine if |start| is within this child. As a special case, if | |
| 556 // the affinity is upstream, then the cursor position between two | |
| 557 // lines belongs to the previous line. | |
| 558 bool start_index_within_child = start < child_length; | |
| 559 if (start == child_length && | |
| 560 !child->IsNextSiblingOnSameLine() && | |
| 561 affinity == ui::AX_TEXT_AFFINITY_UPSTREAM) { | |
| 562 start_index_within_child = true; | |
| 563 } | |
| 564 | |
| 565 // Stop when we reach both the child containing our start offset and, in | |
| 566 // case we are searching forward, the child that is at the end of the line | |
| 567 // on which this object is located. | |
| 568 if (start_index_within_child && (direction == ui::BACKWARDS_DIRECTION || | |
| 569 !child->IsNextSiblingOnSameLine())) { | |
| 570 // Recurse into the inline text boxes. | |
| 571 if (child->GetRole() == ui::AX_ROLE_STATIC_TEXT) { | |
| 572 switch (direction) { | |
| 573 case ui::FORWARDS_DIRECTION: | |
| 574 line_length += child->GetLineStartBoundary( | |
| 575 std::max(start, 0), direction, affinity); | |
| 576 break; | |
| 577 case ui::BACKWARDS_DIRECTION: | |
| 578 line_start += child->GetLineStartBoundary( | |
| 579 std::max(start, 0), direction, affinity); | |
| 580 break; | |
| 581 } | |
| 582 } else { | |
| 583 line_length += child_length; | |
| 584 } | |
| 585 | |
| 586 break; | |
| 587 } | |
| 588 line_length += child_length; | |
| 589 | |
| 590 if (!child->IsNextSiblingOnSameLine()) { | |
| 591 // We are on a new line. | |
| 592 line_start += line_length; | |
| 593 line_length = 0; | |
| 594 } | |
| 595 | |
| 596 start -= child_length; | |
| 597 } | |
| 598 | |
| 599 switch (direction) { | |
| 600 case ui::FORWARDS_DIRECTION: | |
| 601 return line_start + line_length; | |
| 602 case ui::BACKWARDS_DIRECTION: | |
| 603 return line_start; | |
| 604 } | |
| 605 NOTREACHED(); | |
| 606 return 0; | |
| 607 } | |
| 608 | |
| 609 int BrowserAccessibility::GetWordStartBoundary( | |
| 610 int start, ui::TextBoundaryDirection direction) const { | |
| 611 DCHECK_GE(start, -1); | |
| 612 // Special offset that indicates that a word boundary has not been found. | |
| 613 int word_start_not_found = static_cast<int>(GetText().size()); | |
| 614 int word_start = word_start_not_found; | |
| 615 | |
| 616 switch (GetRole()) { | |
| 617 case ui::AX_ROLE_STATIC_TEXT: { | |
| 618 int prev_word_start = word_start_not_found; | |
| 619 int child_start = 0; | |
| 620 int child_end = 0; | |
| 621 | |
| 622 // Go through the inline text boxes. | |
| 623 for (size_t i = 0; i < InternalChildCount(); ++i) { | |
| 624 // The next child starts where the previous one ended. | |
| 625 child_start = child_end; | |
| 626 const BrowserAccessibility* child = InternalGetChild(i); | |
| 627 DCHECK_EQ(child->GetRole(), ui::AX_ROLE_INLINE_TEXT_BOX); | |
| 628 int child_len = static_cast<int>(child->GetText().size()); | |
| 629 child_end += child_len; // End is one past the last character. | |
| 630 | |
| 631 const std::vector<int32_t>& word_starts = | |
| 632 child->GetIntListAttribute(ui::AX_ATTR_WORD_STARTS); | |
| 633 if (word_starts.empty()) { | |
| 634 word_start = child_end; | |
| 635 continue; | |
| 636 } | |
| 637 | |
| 638 int local_start = start - child_start; | |
| 639 std::vector<int32_t>::const_iterator iter = std::upper_bound( | |
| 640 word_starts.begin(), word_starts.end(), local_start); | |
| 641 if (iter != word_starts.end()) { | |
| 642 if (direction == ui::FORWARDS_DIRECTION) { | |
| 643 word_start = child_start + *iter; | |
| 644 } else if (direction == ui::BACKWARDS_DIRECTION) { | |
| 645 if (iter == word_starts.begin()) { | |
| 646 // Return the position of the last word in the previous child. | |
| 647 word_start = prev_word_start; | |
| 648 } else { | |
| 649 word_start = child_start + *(iter - 1); | |
| 650 } | |
| 651 } else { | |
| 652 NOTREACHED(); | |
| 653 } | |
| 654 break; | |
| 655 } | |
| 656 | |
| 657 // No word start that is greater than the requested offset has been | |
| 658 // found. | |
| 659 prev_word_start = child_start + *(iter - 1); | |
| 660 if (direction == ui::FORWARDS_DIRECTION) { | |
| 661 word_start = child_end; | |
| 662 } else if (direction == ui::BACKWARDS_DIRECTION) { | |
| 663 word_start = prev_word_start; | |
| 664 } else { | |
| 665 NOTREACHED(); | |
| 666 } | |
| 667 } | |
| 668 return word_start; | |
| 669 } | |
| 670 | |
| 671 case ui::AX_ROLE_LINE_BREAK: | |
| 672 // Words never start at a line break. | |
| 673 return word_start_not_found; | |
| 674 | |
| 675 default: | |
| 676 // If there are no children, the word start boundary is still unknown or | |
| 677 // found previously depending on the direction. | |
| 678 if (!InternalChildCount()) | |
| 679 return word_start_not_found; | |
| 680 | |
| 681 const BrowserAccessibility* this_object = this; | |
| 682 // Standard text fields such as textarea have an embedded div inside them | |
| 683 // that should be skipped. | |
| 684 // TODO(nektar): This is fragile. Replace with code that flattens tree. | |
| 685 if (IsSimpleTextControl() && InternalChildCount() == 1) { | |
| 686 this_object = InternalGetChild(0); | |
| 687 } | |
| 688 int child_start = 0; | |
| 689 for (size_t i = 0; i < this_object->InternalChildCount(); ++i) { | |
| 690 BrowserAccessibility* child = this_object->InternalGetChild(i); | |
| 691 // Child objects are of length one, since they are represented by a | |
| 692 // single embedded object character. The exception is text-only objects. | |
| 693 int child_len = 1; | |
| 694 if (child->IsTextOnlyObject()) { | |
| 695 child_len = static_cast<int>(child->GetText().length()); | |
| 696 int child_word_start = child->GetWordStartBoundary(start, direction); | |
| 697 if (child_word_start < child_len) { | |
| 698 // We have found a possible word boundary. | |
| 699 word_start = child_start + child_word_start; | |
| 700 } | |
| 701 | |
| 702 // Decide when to stop searching. | |
| 703 if ((word_start != word_start_not_found && | |
| 704 direction == ui::FORWARDS_DIRECTION) || | |
| 705 (start < child_len && direction == ui::BACKWARDS_DIRECTION)) { | |
| 706 break; | |
| 707 } | |
| 708 } | |
| 709 | |
| 710 child_start += child_len; | |
| 711 if (start >= child_len) | |
| 712 start -= child_len; | |
| 713 else | |
| 714 start = -1; | |
| 715 } | |
| 716 return word_start; | |
| 717 } | |
| 718 } | |
| 719 | |
| 720 BrowserAccessibility* BrowserAccessibility::ApproximateHitTest( | 529 BrowserAccessibility* BrowserAccessibility::ApproximateHitTest( |
| 721 const gfx::Point& point) { | 530 const gfx::Point& point) { |
| 722 // The best result found that's a child of this object. | 531 // The best result found that's a child of this object. |
| 723 BrowserAccessibility* child_result = NULL; | 532 BrowserAccessibility* child_result = NULL; |
| 724 // The best result that's an indirect descendant like grandchild, etc. | 533 // The best result that's an indirect descendant like grandchild, etc. |
| 725 BrowserAccessibility* descendant_result = NULL; | 534 BrowserAccessibility* descendant_result = NULL; |
| 726 | 535 |
| 727 // Walk the children recursively looking for the BrowserAccessibility that | 536 // Walk the children recursively looking for the BrowserAccessibility that |
| 728 // most tightly encloses the specified point. Walk backwards so that in | 537 // most tightly encloses the specified point. Walk backwards so that in |
| 729 // the absence of any other information, we assume the object that occurs | 538 // the absence of any other information, we assume the object that occurs |
| (...skipping 397 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1127 return name; | 936 return name; |
| 1128 } | 937 } |
| 1129 | 938 |
| 1130 std::vector<int> BrowserAccessibility::GetLineStartOffsets() const { | 939 std::vector<int> BrowserAccessibility::GetLineStartOffsets() const { |
| 1131 if (!instance_active()) | 940 if (!instance_active()) |
| 1132 return std::vector<int>(); | 941 return std::vector<int>(); |
| 1133 return node()->GetOrComputeLineStartOffsets(); | 942 return node()->GetOrComputeLineStartOffsets(); |
| 1134 } | 943 } |
| 1135 | 944 |
| 1136 BrowserAccessibility::AXPlatformPositionInstance | 945 BrowserAccessibility::AXPlatformPositionInstance |
| 1137 BrowserAccessibility::CreatePositionAt(int offset) const { | 946 BrowserAccessibility::CreatePositionAt(int offset, |
| 947 ui::AXTextAffinity affinity) const { |
| 1138 DCHECK(manager_); | 948 DCHECK(manager_); |
| 1139 return AXPlatformPosition::CreateTextPosition( | 949 return AXPlatformPosition::CreateTextPosition(manager_->ax_tree_id(), GetId(), |
| 1140 manager_->ax_tree_id(), GetId(), offset, ui::AX_TEXT_AFFINITY_DOWNSTREAM); | 950 offset, affinity); |
| 1141 } | 951 } |
| 1142 | 952 |
| 1143 base::string16 BrowserAccessibility::GetInnerText() const { | 953 base::string16 BrowserAccessibility::GetInnerText() const { |
| 1144 if (IsTextOnlyObject()) | 954 if (IsTextOnlyObject()) |
| 1145 return GetString16Attribute(ui::AX_ATTR_NAME); | 955 return GetString16Attribute(ui::AX_ATTR_NAME); |
| 1146 | 956 |
| 1147 base::string16 text; | 957 base::string16 text; |
| 1148 for (size_t i = 0; i < InternalChildCount(); ++i) | 958 for (size_t i = 0; i < InternalChildCount(); ++i) |
| 1149 text += InternalGetChild(i)->GetInnerText(); | 959 text += InternalGetChild(i)->GetInnerText(); |
| 1150 return text; | 960 return text; |
| (...skipping 118 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1269 const ui::AXActionData& data) { | 1079 const ui::AXActionData& data) { |
| 1270 NOTREACHED(); | 1080 NOTREACHED(); |
| 1271 return false; | 1081 return false; |
| 1272 } | 1082 } |
| 1273 | 1083 |
| 1274 void BrowserAccessibility::DoDefaultAction() { | 1084 void BrowserAccessibility::DoDefaultAction() { |
| 1275 NOTREACHED(); | 1085 NOTREACHED(); |
| 1276 } | 1086 } |
| 1277 | 1087 |
| 1278 } // namespace content | 1088 } // namespace content |
| OLD | NEW |