| 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 #include <iterator> | 10 #include <iterator> |
| (...skipping 509 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 |
| OLD | NEW |