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 |