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

Side by Side Diff: content/browser/accessibility/browser_accessibility.cc

Issue 2806773002: Switched to using |AXPosition| for calculating word and line boundaries on Windows. (Closed)
Patch Set: Fixed line boundaries. Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698