| 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 <algorithm> | 7 #include <algorithm> |
| 8 | 8 |
| 9 #include "base/logging.h" | 9 #include "base/logging.h" |
| 10 #include "base/strings/string_number_conversions.h" | 10 #include "base/strings/string_number_conversions.h" |
| (...skipping 244 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 255 const std::vector<int32>& character_offsets = child->GetIntListAttribute( | 255 const std::vector<int32>& character_offsets = child->GetIntListAttribute( |
| 256 ui::AX_ATTR_CHARACTER_OFFSETS); | 256 ui::AX_ATTR_CHARACTER_OFFSETS); |
| 257 int start_pixel_offset = | 257 int start_pixel_offset = |
| 258 local_start > 0 ? character_offsets[local_start - 1] : 0; | 258 local_start > 0 ? character_offsets[local_start - 1] : 0; |
| 259 int end_pixel_offset = | 259 int end_pixel_offset = |
| 260 local_end > 0 ? character_offsets[local_end - 1] : 0; | 260 local_end > 0 ? character_offsets[local_end - 1] : 0; |
| 261 | 261 |
| 262 gfx::Rect child_overlap_rect; | 262 gfx::Rect child_overlap_rect; |
| 263 switch (text_direction) { | 263 switch (text_direction) { |
| 264 case ui::AX_TEXT_DIRECTION_NONE: | 264 case ui::AX_TEXT_DIRECTION_NONE: |
| 265 case ui::AX_TEXT_DIRECTION_LR: { | 265 case ui::AX_TEXT_DIRECTION_LTR: { |
| 266 int left = child_rect.x() + start_pixel_offset; | 266 int left = child_rect.x() + start_pixel_offset; |
| 267 int right = child_rect.x() + end_pixel_offset; | 267 int right = child_rect.x() + end_pixel_offset; |
| 268 child_overlap_rect = gfx::Rect(left, child_rect.y(), | 268 child_overlap_rect = gfx::Rect(left, child_rect.y(), |
| 269 right - left, child_rect.height()); | 269 right - left, child_rect.height()); |
| 270 break; | 270 break; |
| 271 } | 271 } |
| 272 case ui::AX_TEXT_DIRECTION_RL: { | 272 case ui::AX_TEXT_DIRECTION_RTL: { |
| 273 int right = child_rect.right() - start_pixel_offset; | 273 int right = child_rect.right() - start_pixel_offset; |
| 274 int left = child_rect.right() - end_pixel_offset; | 274 int left = child_rect.right() - end_pixel_offset; |
| 275 child_overlap_rect = gfx::Rect(left, child_rect.y(), | 275 child_overlap_rect = gfx::Rect(left, child_rect.y(), |
| 276 right - left, child_rect.height()); | 276 right - left, child_rect.height()); |
| 277 break; | 277 break; |
| 278 } | 278 } |
| 279 case ui::AX_TEXT_DIRECTION_TB: { | 279 case ui::AX_TEXT_DIRECTION_TTB: { |
| 280 int top = child_rect.y() + start_pixel_offset; | 280 int top = child_rect.y() + start_pixel_offset; |
| 281 int bottom = child_rect.y() + end_pixel_offset; | 281 int bottom = child_rect.y() + end_pixel_offset; |
| 282 child_overlap_rect = gfx::Rect(child_rect.x(), top, | 282 child_overlap_rect = gfx::Rect(child_rect.x(), top, |
| 283 child_rect.width(), bottom - top); | 283 child_rect.width(), bottom - top); |
| 284 break; | 284 break; |
| 285 } | 285 } |
| 286 case ui::AX_TEXT_DIRECTION_BT: { | 286 case ui::AX_TEXT_DIRECTION_BTT: { |
| 287 int bottom = child_rect.bottom() - start_pixel_offset; | 287 int bottom = child_rect.bottom() - start_pixel_offset; |
| 288 int top = child_rect.bottom() - end_pixel_offset; | 288 int top = child_rect.bottom() - end_pixel_offset; |
| 289 child_overlap_rect = gfx::Rect(child_rect.x(), top, | 289 child_overlap_rect = gfx::Rect(child_rect.x(), top, |
| 290 child_rect.width(), bottom - top); | 290 child_rect.width(), bottom - top); |
| 291 break; | 291 break; |
| 292 } | 292 } |
| 293 default: | 293 default: |
| 294 NOTREACHED(); | 294 NOTREACHED(); |
| 295 } | 295 } |
| 296 | 296 |
| (...skipping 11 matching lines...) Expand all Loading... |
| 308 gfx::Rect bounds = GetLocalBoundsForRange(start, len); | 308 gfx::Rect bounds = GetLocalBoundsForRange(start, len); |
| 309 | 309 |
| 310 // Adjust the bounds by the top left corner of the containing view's bounds | 310 // Adjust the bounds by the top left corner of the containing view's bounds |
| 311 // in screen coordinates. | 311 // in screen coordinates. |
| 312 bounds.Offset(manager_->GetViewBounds().OffsetFromOrigin()); | 312 bounds.Offset(manager_->GetViewBounds().OffsetFromOrigin()); |
| 313 | 313 |
| 314 return bounds; | 314 return bounds; |
| 315 } | 315 } |
| 316 | 316 |
| 317 int BrowserAccessibility::GetWordStartBoundary( | 317 int BrowserAccessibility::GetWordStartBoundary( |
| 318 int start, ui::TextBoundaryDirection direction) const { | 318 int offset, ui::TextBoundaryDirection direction) const { |
| 319 DCHECK_GE(start, -1); | 319 DCHECK_GE(offset, -1); |
| 320 int text_len = GetStaticTextLenRecursive(); |
| 321 DCHECK_LE(offset, text_len); |
| 322 |
| 320 // Special offset that indicates that a word boundary has not been found. | 323 // Special offset that indicates that a word boundary has not been found. |
| 321 int word_start_not_found = GetStaticTextLenRecursive(); | 324 int word_start_not_found = text_len; |
| 322 int word_start = word_start_not_found; | 325 int word_start = word_start_not_found; |
| 323 | 326 |
| 324 switch (GetRole()) { | 327 switch (GetRole()) { |
| 325 case ui::AX_ROLE_STATIC_TEXT: { | 328 case ui::AX_ROLE_STATIC_TEXT: { |
| 326 int prev_word_start = word_start_not_found; | 329 int prev_word_start = word_start_not_found; |
| 327 int child_start = 0; | 330 int child_start = 0; |
| 328 int child_end = 0; | 331 int child_end = 0; |
| 329 | 332 |
| 330 // Go through the inline text boxes. | 333 // Go through the inline text boxes. |
| 331 for (size_t i = 0; i < InternalChildCount(); ++i) { | 334 for (size_t i = 0; i < InternalChildCount(); ++i) { |
| 332 // The next child starts where the previous one ended. | 335 // The next child starts where the previous one ended. |
| 333 child_start = child_end; | 336 child_start = child_end; |
| 334 BrowserAccessibility* child = InternalGetChild(i); | 337 BrowserAccessibility* child = InternalGetChild(i); |
| 335 DCHECK_EQ(child->GetRole(), ui::AX_ROLE_INLINE_TEXT_BOX); | 338 if (child->GetRole(), ui::AX_ROLE_INLINE_TEXT_BOX) { |
| 336 const std::string& child_text = child->GetStringAttribute( | 339 LOG(WARNING) << "Found child of AX_ROLE_STATIC_TEXT that is not " << |
| 337 ui::AX_ATTR_VALUE); | 340 "AX_ROLE_INLINE_TEXT_BOX. ID=" child->GetId()); |
| 338 int child_len = static_cast<int>(child_text.size()); | 341 continue; |
| 339 child_end += child_len; // End is one past the last character. | 342 } |
| 343 int child_len = GetStaticTextLenRecursive(); |
| 344 child_end += child_len; |
| 340 | 345 |
| 341 const std::vector<int32>& word_starts = child->GetIntListAttribute( | 346 const std::vector<int32>& word_starts = child->GetIntListAttribute( |
| 342 ui::AX_ATTR_WORD_STARTS); | 347 ui::AX_ATTR_WORD_STARTS); |
| 343 if (word_starts.empty()) { | 348 if (word_starts.empty()) { |
| 344 word_start = child_end; | 349 word_start = child_end; |
| 345 continue; | 350 continue; |
| 346 } | 351 } |
| 347 | 352 |
| 348 int local_start = start - child_start; | 353 int local_offset = offset - child_start; |
| 349 std::vector<int32>::const_iterator iter = std::upper_bound( | 354 std::vector<int32>::const_iterator iter = std::upper_bound( |
| 350 word_starts.begin(), word_starts.end(), local_start); | 355 word_starts.begin(), word_starts.end(), local_offset); |
| 351 if (iter != word_starts.end()) { | 356 if (iter != word_starts.end()) { |
| 352 if (direction == ui::FORWARDS_DIRECTION) { | 357 if (direction == ui::FORWARDS_DIRECTION) { |
| 353 word_start = child_start + *iter; | 358 word_start = child_start + *iter; |
| 354 } else if (direction == ui::BACKWARDS_DIRECTION) { | 359 } else if (direction == ui::BACKWARDS_DIRECTION) { |
| 355 if (iter == word_starts.begin()) { | 360 if (iter == word_starts.begin()) { |
| 356 // Return the position of the last word in the previous child. | 361 // Return the position of the last word in the previous child. |
| 357 word_start = prev_word_start; | 362 word_start = prev_word_start; |
| 358 } else { | 363 } else { |
| 359 word_start = child_start + *(iter - 1); | 364 word_start = child_start + *(iter - 1); |
| 360 } | 365 } |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 394 int child_len = child->GetStaticTextLenRecursive(); | 399 int child_len = child->GetStaticTextLenRecursive(); |
| 395 int child_word_start = child->GetWordStartBoundary(start, direction); | 400 int child_word_start = child->GetWordStartBoundary(start, direction); |
| 396 if (child_word_start < child_len) { | 401 if (child_word_start < child_len) { |
| 397 // We have found a possible word boundary. | 402 // We have found a possible word boundary. |
| 398 word_start = child_start + child_word_start; | 403 word_start = child_start + child_word_start; |
| 399 } | 404 } |
| 400 | 405 |
| 401 // Decide when to stop searching. | 406 // Decide when to stop searching. |
| 402 if ((word_start != word_start_not_found && | 407 if ((word_start != word_start_not_found && |
| 403 direction == ui::FORWARDS_DIRECTION) || | 408 direction == ui::FORWARDS_DIRECTION) || |
| 404 (start < child_len && | 409 (offset < child_len && |
| 405 direction == ui::BACKWARDS_DIRECTION)) { | 410 direction == ui::BACKWARDS_DIRECTION)) { |
| 406 break; | 411 break; |
| 407 } | 412 } |
| 408 | 413 |
| 409 child_start += child_len; | 414 child_start += child_len; |
| 410 if (start >= child_len) | 415 if (offset >= child_len) |
| 411 start -= child_len; | 416 offset -= child_len; |
| 412 else | 417 else |
| 413 start = -1; | 418 offset = -1; |
| 414 } | 419 } |
| 415 return word_start; | 420 return word_start; |
| 416 } | 421 } |
| 417 } | 422 } |
| 418 | 423 |
| 424 int BrowserAccessibility::GetStyleChangeBoundary( |
| 425 int offset, ui::TextBoundaryDirection direction) const { |
| 426 DCHECK_GE(offset, 0); |
| 427 int text_len = GetStaticTextLenRecursive(); |
| 428 DCHECK_LE(offset, text_len); |
| 429 |
| 430 int style_change_start; |
| 431 if (direction == ui::BACKWARDS_DIRECTION) |
| 432 style_change_start = 0; |
| 433 else if (direction == ui::FORWARDS_DIRECTION) |
| 434 style_change_start = text_len; |
| 435 else |
| 436 NOTREACHED(); |
| 437 |
| 438 if (IsTextLeaf()) |
| 439 return style_change_start; |
| 440 |
| 441 BrowserAccessibility* leaf = GetTextLeafAtOffset(offset, &style_change_start); |
| 442 DCHECK(leaf); |
| 443 |
| 444 BrowserAccessibility* sibling = leaf; |
| 445 if (direction == ui::BACKWARDS_DIRECTION) |
| 446 while ((sibling = GetPreviousTextLeaf()) && HaveSameStyle(*leaf, *sibling))
{ |
| 447 style_change_start -= sibling->GetStaticTextLenRecursive(); |
| 448 leaf = sibling; |
| 449 } |
| 450 else if (direction == ui::FORWARDS_DIRECTION) |
| 451 while (sibling = GetNextTextLeaf() && HaveSameStyle(*leaf, *sibling)) { |
| 452 style_change_start += leaf->GetStaticTextLenRecursive(); |
| 453 leaf = sibling; |
| 454 } |
| 455 |
| 456 return style_change_start; |
| 457 } |
| 458 |
| 419 BrowserAccessibility* BrowserAccessibility::BrowserAccessibilityForPoint( | 459 BrowserAccessibility* BrowserAccessibility::BrowserAccessibilityForPoint( |
| 420 const gfx::Point& point) { | 460 const gfx::Point& point) { |
| 421 // The best result found that's a child of this object. | 461 // The best result found that's a child of this object. |
| 422 BrowserAccessibility* child_result = NULL; | 462 BrowserAccessibility* child_result = NULL; |
| 423 // The best result that's an indirect descendant like grandchild, etc. | 463 // The best result that's an indirect descendant like grandchild, etc. |
| 424 BrowserAccessibility* descendant_result = NULL; | 464 BrowserAccessibility* descendant_result = NULL; |
| 425 | 465 |
| 426 // Walk the children recursively looking for the BrowserAccessibility that | 466 // Walk the children recursively looking for the BrowserAccessibility that |
| 427 // most tightly encloses the specified point. Walk backwards so that in | 467 // most tightly encloses the specified point. Walk backwards so that in |
| 428 // the absence of any other information, we assume the object that occurs | 468 // the absence of any other information, we assume the object that occurs |
| (...skipping 323 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 752 if (!parent) | 792 if (!parent) |
| 753 return false; | 793 return false; |
| 754 | 794 |
| 755 BrowserAccessibility* grandparent = parent->GetParent(); | 795 BrowserAccessibility* grandparent = parent->GetParent(); |
| 756 if (!grandparent) | 796 if (!grandparent) |
| 757 return false; | 797 return false; |
| 758 | 798 |
| 759 return grandparent->GetRole() == ui::AX_ROLE_IFRAME_PRESENTATIONAL; | 799 return grandparent->GetRole() == ui::AX_ROLE_IFRAME_PRESENTATIONAL; |
| 760 } | 800 } |
| 761 | 801 |
| 802 // |
| 803 // Protected methods. |
| 804 // |
| 805 |
| 806 BrowserAccessibility* BrowserAccessibility::GetFirstTextLeaf() const { |
| 807 int unused_leaf_offset; |
| 808 return GetTextLeafAtOffset(0, &unused_leaf_offset); |
| 809 } |
| 810 |
| 811 // |
| 812 // Private methods. |
| 813 // |
| 814 |
| 815 // Return true only for objects that are the deepest possible descendants which |
| 816 // include complete text information. |
| 817 // For example, objects with role AX_ROLE_EDIT_FIELD are not the deepest nodes |
| 818 // with complete text information because they have objects with role |
| 819 // AX_ROLE_STATIC_TEXT as their descendants which hold the text information. |
| 820 // However, an object with role AX_ROLE_BUTTON has no AX_ROLE_STATIC_TEXT |
| 821 // descendant and it stores its text information directly. |
| 822 bool BrowserAccessibility::IsTextLeaf() const { |
| 823 switch (GetRole()) { |
| 824 case ui::AX_ROLE_BUTTON: |
| 825 case ui::AX_ROLE_CHECK_BOX: |
| 826 case ui::AX_ROLE_COLOR_WELL: |
| 827 case ui::AX_ROLE_COMBO_BOX: |
| 828 case ui::AX_ROLE_DATE: |
| 829 case ui::AX_ROLE_DATE_TIME: |
| 830 case ui::AX_ROLE_IMAGE: |
| 831 case ui::AX_ROLE_LINE_BREAK: |
| 832 case ui::AX_ROLE_LIST_MARKER: |
| 833 case ui::AX_ROLE_POP_UP_BUTTON: |
| 834 case ui::AX_ROLE_RADIO_BUTTON: |
| 835 case ui::AX_ROLE_SEARCH_BOX: |
| 836 case ui::AX_ROLE_TOGGLE_BUTTON: |
| 837 case ui::AX_ROLE_STATIC_TEXT: |
| 838 return true; |
| 839 default: |
| 840 return false; |
| 841 } |
| 842 } |
| 843 |
| 762 int BrowserAccessibility::GetStaticTextLenRecursive() const { | 844 int BrowserAccessibility::GetStaticTextLenRecursive() const { |
| 763 if (GetRole() == ui::AX_ROLE_STATIC_TEXT || | 845 if (IsTextLeaf()) |
| 764 GetRole() == ui::AX_ROLE_LINE_BREAK) { | |
| 765 return static_cast<int>(GetStringAttribute(ui::AX_ATTR_VALUE).size()); | 846 return static_cast<int>(GetStringAttribute(ui::AX_ATTR_VALUE).size()); |
| 766 } | |
| 767 | 847 |
| 768 int len = 0; | 848 int len = 0; |
| 769 for (size_t i = 0; i < InternalChildCount(); ++i) | 849 for (size_t i = 0; i < InternalChildCount(); ++i) |
| 770 len += InternalGetChild(i)->GetStaticTextLenRecursive(); | 850 len += InternalGetChild(i)->GetStaticTextLenRecursive(); |
| 771 return len; | 851 return len; |
| 772 } | 852 } |
| 773 | 853 |
| 854 // TODO(nektar): Refactor whole class to use this function where appropriate. |
| 855 BrowserAccessibility* BrowserAccessibility::GetTextLeafAtOffset( |
| 856 int offset, |
| 857 int* leaf_start_offset) const { |
| 858 DCHECK_GE(offset, 0); |
| 859 DCHECK_LE(offset, GetStaticTextLenRecursive()); |
| 860 DCHECK(child_start_offset); |
| 861 |
| 862 if (IsTextLeaf()) { |
| 863 *leaf_start_offset = 0; |
| 864 return this; |
| 865 } |
| 866 |
| 867 int child_start = 0; |
| 868 BrowserAccessibility* leaf = nullptr; |
| 869 for (size_t i = 0; i < InternalChildCount(); ++i) { |
| 870 BrowserAccessibility* child = InternalGetChild(i); |
| 871 int child_len = GetStaticTextLenRecursive(); |
| 872 int child_end = child_start + child_len; |
| 873 if (offset < child_end) { |
| 874 leaf = child->GetTextLeafAtOffset(offset, leaf_start_offset); |
| 875 break; |
| 876 } |
| 877 |
| 878 child_start = child_end; |
| 879 offset -= child_len; |
| 880 } |
| 881 |
| 882 *leaf_start_offset += child_start; |
| 883 return leaf; |
| 884 } |
| 885 |
| 886 BrowserAccessibility* BrowserAccessibility::GetLastTextLeaf() const { |
| 887 int text_len = GetStaticTextLenRecursive(); |
| 888 if (text_len <= 0) |
| 889 return GetFirstTextLeaf(); |
| 890 |
| 891 int unused_leaf_offset; |
| 892 return GetTextLeafAtOffset(text_len - 1, &unused_leaf_offset); |
| 893 } |
| 894 |
| 895 BrowserAccessibility* BrowserAccessibility::GetNextTextLeaf() const { |
| 896 if (GetFirstTextLeaf() && GetFirstTextLeaf() != this) |
| 897 return GetNextFirstLeaf(); |
| 898 |
| 899 BrowserAccessibility* next_leaf = nullptr; |
| 900 BrowserAccessibility* current_object = GetNextSibling(); |
| 901 while (!next_leaf && current_object) { |
| 902 next_leaf = current_object->GetFirstTextLeaf(); |
| 903 current_object = current_object->GetNextSibling(); |
| 904 } |
| 905 if (next_leaf) |
| 906 return next_leaf; |
| 907 |
| 908 if (GetParent() && GetParent()->GetNextSibling()) |
| 909 next_leaf = GetParent()->GetNextSibling()->GetNextTextLeaf(); |
| 910 |
| 911 return next_leaf; |
| 912 } |
| 913 |
| 914 BrowserAccessibility* BrowserAccessibility::GetPreviousTextLeaf() const { |
| 915 BrowserAccessibility* previous_leaf = nullptr; |
| 916 BrowserAccessibility* current_object = GetPreviousSibling(); |
| 917 while (!previous_leaf && current_object) { |
| 918 previous_leaf = current_object->GetLastTextLeaf(); |
| 919 current_object = current_object->GetPreviousSibling(); |
| 920 } |
| 921 if (previous_leaf) |
| 922 return previous_leaf; |
| 923 |
| 924 if (GetParent() && GetParent()->GetPreviousSibling()) |
| 925 previous_leaf = GetParent()->GetPreviousSibling()->GetPreviousTextLeaf(); |
| 926 |
| 927 return previous_leaf; |
| 928 } |
| 929 |
| 774 BrowserAccessibility* BrowserAccessibility::GetParentForBoundsCalculation() | 930 BrowserAccessibility* BrowserAccessibility::GetParentForBoundsCalculation() |
| 775 const { | 931 const { |
| 776 if (!node_ || !manager_) | 932 if (!node_ || !manager_) |
| 777 return NULL; | 933 return NULL; |
| 778 ui::AXNode* parent = node_->parent(); | 934 ui::AXNode* parent = node_->parent(); |
| 779 if (parent) | 935 if (parent) |
| 780 return manager_->GetFromAXNode(parent); | 936 return manager_->GetFromAXNode(parent); |
| 781 | 937 |
| 782 if (!manager_->delegate()) | 938 if (!manager_->delegate()) |
| 783 return NULL; | 939 return NULL; |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 818 bounds.Offset(-sx, -sy); | 974 bounds.Offset(-sx, -sy); |
| 819 } | 975 } |
| 820 need_to_offset_web_area = true; | 976 need_to_offset_web_area = true; |
| 821 } | 977 } |
| 822 parent = parent->GetParentForBoundsCalculation(); | 978 parent = parent->GetParentForBoundsCalculation(); |
| 823 } | 979 } |
| 824 | 980 |
| 825 return bounds; | 981 return bounds; |
| 826 } | 982 } |
| 827 | 983 |
| 984 // |
| 985 // Static methods. |
| 986 // |
| 987 |
| 988 bool BrowserAccessibility::HaveSameStyle(BrowserAccessibility& object1, |
| 989 BrowserAccessibility& object2) { |
| 990 |
| 991 } |
| 992 |
| 828 } // namespace content | 993 } // namespace content |
| OLD | NEW |