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 |