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

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 unit tests. 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 #include <iterator> 10 #include <iterator>
(...skipping 509 matching lines...) Expand 10 before | Expand all | Expand 10 after
520 // Some screen readers like Jaws and older versions of VoiceOver require a 520 // Some screen readers like Jaws and older versions of VoiceOver require a
521 // value to be set in text fields with rich content, even though the same 521 // value to be set in text fields with rich content, even though the same
522 // information is available on the children. 522 // information is available on the children.
523 if (value.empty() && 523 if (value.empty() &&
524 (IsSimpleTextControl() || IsRichTextControl()) && 524 (IsSimpleTextControl() || IsRichTextControl()) &&
525 !IsNativeTextControl()) 525 !IsNativeTextControl())
526 value = GetInnerText(); 526 value = GetInnerText();
527 return value; 527 return value;
528 } 528 }
529 529
530 int BrowserAccessibility::GetLineStartBoundary(
531 int start,
532 ui::TextBoundaryDirection direction,
533 ui::AXTextAffinity affinity) const {
534 DCHECK_GE(start, 0);
535 DCHECK_LE(start, static_cast<int>(GetText().length()));
536
537 if (IsSimpleTextControl()) {
538 return ui::FindAccessibleTextBoundary(GetText(), GetLineStartOffsets(),
539 ui::LINE_BOUNDARY, start, direction,
540 affinity);
541 }
542
543 // Keeps track of the start offset of each consecutive line.
544 int line_start = 0;
545 // Keeps track of the length of each consecutive line.
546 int line_length = 0;
547 for (size_t i = 0; i < InternalChildCount(); ++i) {
548 const BrowserAccessibility* child = InternalGetChild(i);
549 DCHECK(child);
550 // Child objects are of length one, since they are represented by a
551 // single embedded object character. The exception is text-only objects.
552 int child_length = 1;
553 if (child->IsTextOnlyObject())
554 child_length = static_cast<int>(child->GetText().length());
555
556 // Determine if |start| is within this child. As a special case, if
557 // the affinity is upstream, then the cursor position between two
558 // lines belongs to the previous line.
559 bool start_index_within_child = start < child_length;
560 if (start == child_length &&
561 !child->IsNextSiblingOnSameLine() &&
562 affinity == ui::AX_TEXT_AFFINITY_UPSTREAM) {
563 start_index_within_child = true;
564 }
565
566 // Stop when we reach both the child containing our start offset and, in
567 // case we are searching forward, the child that is at the end of the line
568 // on which this object is located.
569 if (start_index_within_child && (direction == ui::BACKWARDS_DIRECTION ||
570 !child->IsNextSiblingOnSameLine())) {
571 // Recurse into the inline text boxes.
572 if (child->GetRole() == ui::AX_ROLE_STATIC_TEXT) {
573 switch (direction) {
574 case ui::FORWARDS_DIRECTION:
575 line_length += child->GetLineStartBoundary(
576 std::max(start, 0), direction, affinity);
577 break;
578 case ui::BACKWARDS_DIRECTION:
579 line_start += child->GetLineStartBoundary(
580 std::max(start, 0), direction, affinity);
581 break;
582 }
583 } else {
584 line_length += child_length;
585 }
586
587 break;
588 }
589 line_length += child_length;
590
591 if (!child->IsNextSiblingOnSameLine()) {
592 // We are on a new line.
593 line_start += line_length;
594 line_length = 0;
595 }
596
597 start -= child_length;
598 }
599
600 switch (direction) {
601 case ui::FORWARDS_DIRECTION:
602 return line_start + line_length;
603 case ui::BACKWARDS_DIRECTION:
604 return line_start;
605 }
606 NOTREACHED();
607 return 0;
608 }
609
610 int BrowserAccessibility::GetWordStartBoundary(
611 int start, ui::TextBoundaryDirection direction) const {
612 DCHECK_GE(start, -1);
613 // Special offset that indicates that a word boundary has not been found.
614 int word_start_not_found = static_cast<int>(GetText().size());
615 int word_start = word_start_not_found;
616
617 switch (GetRole()) {
618 case ui::AX_ROLE_STATIC_TEXT: {
619 int prev_word_start = word_start_not_found;
620 int child_start = 0;
621 int child_end = 0;
622
623 // Go through the inline text boxes.
624 for (size_t i = 0; i < InternalChildCount(); ++i) {
625 // The next child starts where the previous one ended.
626 child_start = child_end;
627 const BrowserAccessibility* child = InternalGetChild(i);
628 DCHECK_EQ(child->GetRole(), ui::AX_ROLE_INLINE_TEXT_BOX);
629 int child_len = static_cast<int>(child->GetText().size());
630 child_end += child_len; // End is one past the last character.
631
632 const std::vector<int32_t>& word_starts =
633 child->GetIntListAttribute(ui::AX_ATTR_WORD_STARTS);
634 if (word_starts.empty()) {
635 word_start = child_end;
636 continue;
637 }
638
639 int local_start = start - child_start;
640 std::vector<int32_t>::const_iterator iter = std::upper_bound(
641 word_starts.begin(), word_starts.end(), local_start);
642 if (iter != word_starts.end()) {
643 if (direction == ui::FORWARDS_DIRECTION) {
644 word_start = child_start + *iter;
645 } else if (direction == ui::BACKWARDS_DIRECTION) {
646 if (iter == word_starts.begin()) {
647 // Return the position of the last word in the previous child.
648 word_start = prev_word_start;
649 } else {
650 word_start = child_start + *(iter - 1);
651 }
652 } else {
653 NOTREACHED();
654 }
655 break;
656 }
657
658 // No word start that is greater than the requested offset has been
659 // found.
660 prev_word_start = child_start + *(iter - 1);
661 if (direction == ui::FORWARDS_DIRECTION) {
662 word_start = child_end;
663 } else if (direction == ui::BACKWARDS_DIRECTION) {
664 word_start = prev_word_start;
665 } else {
666 NOTREACHED();
667 }
668 }
669 return word_start;
670 }
671
672 case ui::AX_ROLE_LINE_BREAK:
673 // Words never start at a line break.
674 return word_start_not_found;
675
676 default:
677 // If there are no children, the word start boundary is still unknown or
678 // found previously depending on the direction.
679 if (!InternalChildCount())
680 return word_start_not_found;
681
682 const BrowserAccessibility* this_object = this;
683 // Standard text fields such as textarea have an embedded div inside them
684 // that should be skipped.
685 // TODO(nektar): This is fragile. Replace with code that flattens tree.
686 if (IsSimpleTextControl() && InternalChildCount() == 1) {
687 this_object = InternalGetChild(0);
688 }
689 int child_start = 0;
690 for (size_t i = 0; i < this_object->InternalChildCount(); ++i) {
691 BrowserAccessibility* child = this_object->InternalGetChild(i);
692 // Child objects are of length one, since they are represented by a
693 // single embedded object character. The exception is text-only objects.
694 int child_len = 1;
695 if (child->IsTextOnlyObject()) {
696 child_len = static_cast<int>(child->GetText().length());
697 int child_word_start = child->GetWordStartBoundary(start, direction);
698 if (child_word_start < child_len) {
699 // We have found a possible word boundary.
700 word_start = child_start + child_word_start;
701 }
702
703 // Decide when to stop searching.
704 if ((word_start != word_start_not_found &&
705 direction == ui::FORWARDS_DIRECTION) ||
706 (start < child_len && direction == ui::BACKWARDS_DIRECTION)) {
707 break;
708 }
709 }
710
711 child_start += child_len;
712 if (start >= child_len)
713 start -= child_len;
714 else
715 start = -1;
716 }
717 return word_start;
718 }
719 }
720
721 BrowserAccessibility* BrowserAccessibility::ApproximateHitTest( 530 BrowserAccessibility* BrowserAccessibility::ApproximateHitTest(
722 const gfx::Point& point) { 531 const gfx::Point& point) {
723 // The best result found that's a child of this object. 532 // The best result found that's a child of this object.
724 BrowserAccessibility* child_result = NULL; 533 BrowserAccessibility* child_result = NULL;
725 // The best result that's an indirect descendant like grandchild, etc. 534 // The best result that's an indirect descendant like grandchild, etc.
726 BrowserAccessibility* descendant_result = NULL; 535 BrowserAccessibility* descendant_result = NULL;
727 536
728 // Walk the children recursively looking for the BrowserAccessibility that 537 // Walk the children recursively looking for the BrowserAccessibility that
729 // most tightly encloses the specified point. Walk backwards so that in 538 // most tightly encloses the specified point. Walk backwards so that in
730 // the absence of any other information, we assume the object that occurs 539 // the absence of any other information, we assume the object that occurs
(...skipping 505 matching lines...) Expand 10 before | Expand all | Expand 10 after
1236 return name; 1045 return name;
1237 } 1046 }
1238 1047
1239 std::vector<int> BrowserAccessibility::GetLineStartOffsets() const { 1048 std::vector<int> BrowserAccessibility::GetLineStartOffsets() const {
1240 if (!instance_active()) 1049 if (!instance_active())
1241 return std::vector<int>(); 1050 return std::vector<int>();
1242 return node()->GetOrComputeLineStartOffsets(); 1051 return node()->GetOrComputeLineStartOffsets();
1243 } 1052 }
1244 1053
1245 BrowserAccessibility::AXPlatformPositionInstance 1054 BrowserAccessibility::AXPlatformPositionInstance
1246 BrowserAccessibility::CreatePositionAt(int offset) const { 1055 BrowserAccessibility::CreatePositionAt(int offset,
1056 ui::AXTextAffinity affinity) const {
1247 DCHECK(manager_); 1057 DCHECK(manager_);
1248 return AXPlatformPosition::CreateTextPosition( 1058 return AXPlatformPosition::CreateTextPosition(manager_->ax_tree_id(), GetId(),
1249 manager_->ax_tree_id(), GetId(), offset, ui::AX_TEXT_AFFINITY_DOWNSTREAM); 1059 offset, affinity);
1250 } 1060 }
1251 1061
1252 base::string16 BrowserAccessibility::GetInnerText() const { 1062 base::string16 BrowserAccessibility::GetInnerText() const {
1253 if (IsTextOnlyObject()) 1063 if (IsTextOnlyObject())
1254 return GetString16Attribute(ui::AX_ATTR_NAME); 1064 return GetString16Attribute(ui::AX_ATTR_NAME);
1255 1065
1256 base::string16 text; 1066 base::string16 text;
1257 for (size_t i = 0; i < InternalChildCount(); ++i) 1067 for (size_t i = 0; i < InternalChildCount(); ++i)
1258 text += InternalGetChild(i)->GetInnerText(); 1068 text += InternalGetChild(i)->GetInnerText();
1259 return text; 1069 return text;
(...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after
1379 return gfx::kNullAcceleratedWidget; 1189 return gfx::kNullAcceleratedWidget;
1380 } 1190 }
1381 1191
1382 bool BrowserAccessibility::AccessibilityPerformAction( 1192 bool BrowserAccessibility::AccessibilityPerformAction(
1383 const ui::AXActionData& data) { 1193 const ui::AXActionData& data) {
1384 NOTREACHED(); 1194 NOTREACHED();
1385 return false; 1195 return false;
1386 } 1196 }
1387 1197
1388 } // namespace content 1198 } // namespace content
OLDNEW
« no previous file with comments | « content/browser/accessibility/browser_accessibility.h ('k') | content/browser/accessibility/browser_accessibility_manager.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698