| 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 | 10 |
| (...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 97 return false; | 97 return false; |
| 98 } | 98 } |
| 99 | 99 |
| 100 bool BrowserAccessibility::IsTextOnlyObject() const { | 100 bool BrowserAccessibility::IsTextOnlyObject() const { |
| 101 return GetRole() == ui::AX_ROLE_STATIC_TEXT || | 101 return GetRole() == ui::AX_ROLE_STATIC_TEXT || |
| 102 GetRole() == ui::AX_ROLE_LINE_BREAK; | 102 GetRole() == ui::AX_ROLE_LINE_BREAK; |
| 103 } | 103 } |
| 104 | 104 |
| 105 BrowserAccessibility* BrowserAccessibility::PlatformGetChild( | 105 BrowserAccessibility* BrowserAccessibility::PlatformGetChild( |
| 106 uint32_t child_index) const { | 106 uint32_t child_index) const { |
| 107 DCHECK(child_index < PlatformChildCount()); | 107 DCHECK_LT(child_index, PlatformChildCount()); |
| 108 BrowserAccessibility* result = nullptr; | 108 BrowserAccessibility* result = nullptr; |
| 109 | 109 |
| 110 if (HasIntAttribute(ui::AX_ATTR_CHILD_TREE_ID)) { | 110 if (HasIntAttribute(ui::AX_ATTR_CHILD_TREE_ID)) { |
| 111 BrowserAccessibilityManager* child_manager = | 111 BrowserAccessibilityManager* child_manager = |
| 112 BrowserAccessibilityManager::FromID( | 112 BrowserAccessibilityManager::FromID( |
| 113 GetIntAttribute(ui::AX_ATTR_CHILD_TREE_ID)); | 113 GetIntAttribute(ui::AX_ATTR_CHILD_TREE_ID)); |
| 114 if (child_manager) | 114 if (child_manager) |
| 115 result = child_manager->GetRoot(); | 115 result = child_manager->GetRoot(); |
| 116 } else { | 116 } else { |
| 117 result = InternalGetChild(child_index); | 117 result = InternalGetChild(child_index); |
| (...skipping 135 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 253 | 253 |
| 254 // Adjust the bounds by the top left corner of the containing view's bounds | 254 // Adjust the bounds by the top left corner of the containing view's bounds |
| 255 // in screen coordinates. | 255 // in screen coordinates. |
| 256 bounds.Offset(manager_->GetViewBounds().OffsetFromOrigin()); | 256 bounds.Offset(manager_->GetViewBounds().OffsetFromOrigin()); |
| 257 | 257 |
| 258 return bounds; | 258 return bounds; |
| 259 } | 259 } |
| 260 | 260 |
| 261 gfx::Rect BrowserAccessibility::GetLocalBoundsForRange(int start, int len) | 261 gfx::Rect BrowserAccessibility::GetLocalBoundsForRange(int start, int len) |
| 262 const { | 262 const { |
| 263 // Standard text fields such as textarea have an embedded div inside them that |
| 264 // holds all the text. |
| 265 if (IsSimpleTextControl() && InternalChildCount() == 1) |
| 266 return InternalGetChild(0)->GetLocalBoundsForRange(start, len); |
| 267 |
| 263 if (GetRole() != ui::AX_ROLE_STATIC_TEXT) { | 268 if (GetRole() != ui::AX_ROLE_STATIC_TEXT) { |
| 264 // Apply recursively to all static text descendants. For example, if | |
| 265 // you call it on a div with two text node children, it just calls | |
| 266 // GetLocalBoundsForRange on each of the two children (adjusting | |
| 267 // |start| for each one) and unions the resulting rects. | |
| 268 gfx::Rect bounds; | 269 gfx::Rect bounds; |
| 269 for (size_t i = 0; i < InternalChildCount(); ++i) { | 270 for (size_t i = 0; i < InternalChildCount() && start >= 0; ++i) { |
| 270 BrowserAccessibility* child = InternalGetChild(i); | 271 BrowserAccessibility* child = InternalGetChild(i); |
| 271 int child_len = child->GetInnerTextLength(); | 272 // Child objects are of length one, since they are represented by a single |
| 272 if (start < child_len && start + len > 0) { | 273 // special character. The exception is text-only objects. |
| 273 gfx::Rect child_rect = child->GetLocalBoundsForRange(start, len); | 274 int child_length_in_parent = 1; |
| 275 if (child->IsTextOnlyObject()) |
| 276 child_length_in_parent = static_cast<int>(child->GetText().size()); |
| 277 if (start < child_length_in_parent) { |
| 278 gfx::Rect child_rect; |
| 279 if (child->IsTextOnlyObject()) |
| 280 child_rect = child->GetLocalBoundsForRange(start, len); |
| 281 else |
| 282 child_rect = child->GetLocalBoundsForRange( |
| 283 start, static_cast<int>(child->GetText().size())); |
| 274 bounds.Union(child_rect); | 284 bounds.Union(child_rect); |
| 285 len -= child_length_in_parent; |
| 275 } | 286 } |
| 276 start -= child_len; | 287 start -= child_length_in_parent; |
| 277 } | 288 } |
| 278 return ElementBoundsToLocalBounds(bounds); | 289 return ElementBoundsToLocalBounds(bounds); |
| 279 } | 290 } |
| 280 | 291 |
| 281 int end = start + len; | 292 int end = start + len; |
| 282 int child_start = 0; | 293 int child_start = 0; |
| 283 int child_end = 0; | 294 int child_end = 0; |
| 284 | 295 |
| 285 gfx::Rect bounds; | 296 gfx::Rect bounds; |
| 286 for (size_t i = 0; i < InternalChildCount() && child_end < start + len; ++i) { | 297 for (size_t i = 0; i < InternalChildCount() && child_end < start + len; ++i) { |
| 287 BrowserAccessibility* child = InternalGetChild(i); | 298 BrowserAccessibility* child = InternalGetChild(i); |
| 288 if (child->GetRole() != ui::AX_ROLE_INLINE_TEXT_BOX) { | 299 if (child->GetRole() != ui::AX_ROLE_INLINE_TEXT_BOX) { |
| 289 DLOG(WARNING) << "BrowserAccessibility objects with role STATIC_TEXT " << | 300 DLOG(WARNING) << "BrowserAccessibility objects with role STATIC_TEXT " << |
| 290 "should have children of role INLINE_TEXT_BOX."; | 301 "should have children of role INLINE_TEXT_BOX."; |
| 291 continue; | 302 continue; |
| 292 } | 303 } |
| 293 | 304 |
| 294 std::string child_text; | 305 int child_len = static_cast<int>(GetText().size()); |
| 295 child->GetStringAttribute(ui::AX_ATTR_NAME, &child_text); | |
| 296 int child_len = static_cast<int>(child_text.size()); | |
| 297 child_start = child_end; | 306 child_start = child_end; |
| 298 child_end += child_len; | 307 child_end += child_len; |
| 299 | 308 |
| 300 if (child_end < start) | 309 if (child_end < start) |
| 301 continue; | 310 continue; |
| 302 | 311 |
| 303 int overlap_start = std::max(start, child_start); | 312 int overlap_start = std::max(start, child_start); |
| 304 int overlap_end = std::min(end, child_end); | 313 int overlap_end = std::min(end, child_end); |
| 305 | 314 |
| 306 int local_start = overlap_start - child_start; | 315 int local_start = overlap_start - child_start; |
| (...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 375 base::string16 value = GetString16Attribute(ui::AX_ATTR_VALUE); | 384 base::string16 value = GetString16Attribute(ui::AX_ATTR_VALUE); |
| 376 if (value.empty() && IsSimpleTextControl()) | 385 if (value.empty() && IsSimpleTextControl()) |
| 377 value = GetInnerText(); | 386 value = GetInnerText(); |
| 378 return value; | 387 return value; |
| 379 } | 388 } |
| 380 | 389 |
| 381 int BrowserAccessibility::GetWordStartBoundary( | 390 int BrowserAccessibility::GetWordStartBoundary( |
| 382 int start, ui::TextBoundaryDirection direction) const { | 391 int start, ui::TextBoundaryDirection direction) const { |
| 383 DCHECK_GE(start, -1); | 392 DCHECK_GE(start, -1); |
| 384 // Special offset that indicates that a word boundary has not been found. | 393 // Special offset that indicates that a word boundary has not been found. |
| 385 int word_start_not_found = GetInnerTextLength(); | 394 int word_start_not_found = static_cast<int>(GetText().size()); |
| 386 int word_start = word_start_not_found; | 395 int word_start = word_start_not_found; |
| 387 | 396 |
| 388 switch (GetRole()) { | 397 switch (GetRole()) { |
| 389 case ui::AX_ROLE_STATIC_TEXT: { | 398 case ui::AX_ROLE_STATIC_TEXT: { |
| 390 int prev_word_start = word_start_not_found; | 399 int prev_word_start = word_start_not_found; |
| 391 int child_start = 0; | 400 int child_start = 0; |
| 392 int child_end = 0; | 401 int child_end = 0; |
| 393 | 402 |
| 394 // Go through the inline text boxes. | 403 // Go through the inline text boxes. |
| 395 for (size_t i = 0; i < InternalChildCount(); ++i) { | 404 for (size_t i = 0; i < InternalChildCount(); ++i) { |
| 396 // The next child starts where the previous one ended. | 405 // The next child starts where the previous one ended. |
| 397 child_start = child_end; | 406 child_start = child_end; |
| 398 BrowserAccessibility* child = InternalGetChild(i); | 407 BrowserAccessibility* child = InternalGetChild(i); |
| 399 DCHECK_EQ(child->GetRole(), ui::AX_ROLE_INLINE_TEXT_BOX); | 408 DCHECK_EQ(child->GetRole(), ui::AX_ROLE_INLINE_TEXT_BOX); |
| 400 const std::string& child_text = child->GetStringAttribute( | 409 int child_len = static_cast<int>(GetText().size()); |
| 401 ui::AX_ATTR_NAME); | |
| 402 int child_len = static_cast<int>(child_text.size()); | |
| 403 child_end += child_len; // End is one past the last character. | 410 child_end += child_len; // End is one past the last character. |
| 404 | 411 |
| 405 const std::vector<int32_t>& word_starts = | 412 const std::vector<int32_t>& word_starts = |
| 406 child->GetIntListAttribute(ui::AX_ATTR_WORD_STARTS); | 413 child->GetIntListAttribute(ui::AX_ATTR_WORD_STARTS); |
| 407 if (word_starts.empty()) { | 414 if (word_starts.empty()) { |
| 408 word_start = child_end; | 415 word_start = child_end; |
| 409 continue; | 416 continue; |
| 410 } | 417 } |
| 411 | 418 |
| 412 int local_start = start - child_start; | 419 int local_start = start - child_start; |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 445 case ui::AX_ROLE_LINE_BREAK: | 452 case ui::AX_ROLE_LINE_BREAK: |
| 446 // Words never start at a line break. | 453 // Words never start at a line break. |
| 447 return word_start_not_found; | 454 return word_start_not_found; |
| 448 | 455 |
| 449 default: | 456 default: |
| 450 // If there are no children, the word start boundary is still unknown or | 457 // If there are no children, the word start boundary is still unknown or |
| 451 // found previously depending on the direction. | 458 // found previously depending on the direction. |
| 452 if (!InternalChildCount()) | 459 if (!InternalChildCount()) |
| 453 return word_start_not_found; | 460 return word_start_not_found; |
| 454 | 461 |
| 462 const BrowserAccessibility* this_object = this; |
| 463 // Standard text fields such as textarea have an embedded div inside them |
| 464 // that should be skipped. |
| 465 if (IsSimpleTextControl() && InternalChildCount() == 1) { |
| 466 this_object = InternalGetChild(0); |
| 467 } |
| 455 int child_start = 0; | 468 int child_start = 0; |
| 456 for (size_t i = 0; i < InternalChildCount(); ++i) { | 469 for (size_t i = 0; i < this_object->InternalChildCount(); ++i) { |
| 457 BrowserAccessibility* child = InternalGetChild(i); | 470 BrowserAccessibility* child = this_object->InternalGetChild(i); |
| 458 int child_len = child->GetInnerTextLength(); | 471 // Child objects are of length one, since they are represented by a |
| 459 int child_word_start = child->GetWordStartBoundary(start, direction); | 472 // single special character. The exception is text-only objects. |
| 460 if (child_word_start < child_len) { | 473 int child_len = 1; |
| 461 // We have found a possible word boundary. | 474 if (child->IsTextOnlyObject()) { |
| 462 word_start = child_start + child_word_start; | 475 child_len = static_cast<int>(child->GetText().size()); |
| 463 } | 476 int child_word_start = child->GetWordStartBoundary(start, direction); |
| 477 if (child_word_start < child_len) { |
| 478 // We have found a possible word boundary. |
| 479 word_start = child_start + child_word_start; |
| 480 } |
| 464 | 481 |
| 465 // Decide when to stop searching. | 482 // Decide when to stop searching. |
| 466 if ((word_start != word_start_not_found && | 483 if ((word_start != word_start_not_found && |
| 467 direction == ui::FORWARDS_DIRECTION) || | 484 direction == ui::FORWARDS_DIRECTION) || |
| 468 (start < child_len && | 485 (start < child_len && direction == ui::BACKWARDS_DIRECTION)) { |
| 469 direction == ui::BACKWARDS_DIRECTION)) { | 486 break; |
| 470 break; | 487 } |
| 471 } | 488 } |
| 472 | 489 |
| 473 child_start += child_len; | 490 child_start += child_len; |
| 474 if (start >= child_len) | 491 if (start >= child_len) |
| 475 start -= child_len; | 492 start -= child_len; |
| 476 else | 493 else |
| 477 start = -1; | 494 start = -1; |
| 478 } | 495 } |
| 479 return word_start; | 496 return word_start; |
| 480 } | 497 } |
| (...skipping 168 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 649 } | 666 } |
| 650 | 667 |
| 651 *is_defined = true; | 668 *is_defined = true; |
| 652 | 669 |
| 653 if (base::EqualsASCII(value, "true")) | 670 if (base::EqualsASCII(value, "true")) |
| 654 return true; | 671 return true; |
| 655 | 672 |
| 656 if (base::EqualsASCII(value, "mixed")) | 673 if (base::EqualsASCII(value, "mixed")) |
| 657 *is_mixed = true; | 674 *is_mixed = true; |
| 658 | 675 |
| 659 return false; // Not set | 676 return false; // Not set. |
| 677 } |
| 678 |
| 679 base::string16 BrowserAccessibility::GetText() const { |
| 680 return GetInnerText(); |
| 660 } | 681 } |
| 661 | 682 |
| 662 bool BrowserAccessibility::HasState(ui::AXState state_enum) const { | 683 bool BrowserAccessibility::HasState(ui::AXState state_enum) const { |
| 663 return (GetState() >> state_enum) & 1; | 684 return (GetState() >> state_enum) & 1; |
| 664 } | 685 } |
| 665 | 686 |
| 666 bool BrowserAccessibility::IsCellOrTableHeaderRole() const { | 687 bool BrowserAccessibility::IsCellOrTableHeaderRole() const { |
| 667 return (GetRole() == ui::AX_ROLE_CELL || | 688 return (GetRole() == ui::AX_ROLE_CELL || |
| 668 GetRole() == ui::AX_ROLE_COLUMN_HEADER || | 689 GetRole() == ui::AX_ROLE_COLUMN_HEADER || |
| 669 GetRole() == ui::AX_ROLE_ROW_HEADER); | 690 GetRole() == ui::AX_ROLE_ROW_HEADER); |
| 670 } | 691 } |
| 671 | 692 |
| 672 bool BrowserAccessibility::HasCaret() const { | 693 bool BrowserAccessibility::HasCaret() const { |
| 673 if (IsEditableText() && !HasState(ui::AX_STATE_RICHLY_EDITABLE) && | 694 if (HasState(ui::AX_STATE_EDITABLE) && |
| 695 !HasState(ui::AX_STATE_RICHLY_EDITABLE) && |
| 674 HasIntAttribute(ui::AX_ATTR_TEXT_SEL_START) && | 696 HasIntAttribute(ui::AX_ATTR_TEXT_SEL_START) && |
| 675 HasIntAttribute(ui::AX_ATTR_TEXT_SEL_END)) { | 697 HasIntAttribute(ui::AX_ATTR_TEXT_SEL_END)) { |
| 676 return true; | 698 return true; |
| 677 } | 699 } |
| 678 | 700 |
| 679 // The caret is always at the focus of the selection. | 701 // The caret is always at the focus of the selection. |
| 680 int32_t focus_id = manager()->GetTreeData().sel_focus_object_id; | 702 int32_t focus_id = manager()->GetTreeData().sel_focus_object_id; |
| 681 BrowserAccessibility* focus_object = manager()->GetFromID(focus_id); | 703 BrowserAccessibility* focus_object = manager()->GetFromID(focus_id); |
| 682 if (!focus_object) | 704 if (!focus_object) |
| 683 return false; | 705 return false; |
| 684 | 706 |
| 685 if (!focus_object->IsDescendantOf(this)) | 707 if (!focus_object->IsDescendantOf(this)) |
| 686 return false; | 708 return false; |
| 687 | 709 |
| 688 return true; | 710 return true; |
| 689 } | 711 } |
| 690 | 712 |
| 691 bool BrowserAccessibility::IsEditableText() const { | |
| 692 return HasState(ui::AX_STATE_EDITABLE); | |
| 693 } | |
| 694 | |
| 695 bool BrowserAccessibility::IsWebAreaForPresentationalIframe() const { | 713 bool BrowserAccessibility::IsWebAreaForPresentationalIframe() const { |
| 696 if (GetRole() != ui::AX_ROLE_WEB_AREA && | 714 if (GetRole() != ui::AX_ROLE_WEB_AREA && |
| 697 GetRole() != ui::AX_ROLE_ROOT_WEB_AREA) { | 715 GetRole() != ui::AX_ROLE_ROOT_WEB_AREA) { |
| 698 return false; | 716 return false; |
| 699 } | 717 } |
| 700 | 718 |
| 701 BrowserAccessibility* parent = GetParent(); | 719 BrowserAccessibility* parent = GetParent(); |
| 702 if (!parent) | 720 if (!parent) |
| 703 return false; | 721 return false; |
| 704 | 722 |
| (...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 785 base::string16 BrowserAccessibility::GetInnerText() const { | 803 base::string16 BrowserAccessibility::GetInnerText() const { |
| 786 if (IsTextOnlyObject()) | 804 if (IsTextOnlyObject()) |
| 787 return GetString16Attribute(ui::AX_ATTR_NAME); | 805 return GetString16Attribute(ui::AX_ATTR_NAME); |
| 788 | 806 |
| 789 base::string16 text; | 807 base::string16 text; |
| 790 for (size_t i = 0; i < InternalChildCount(); ++i) | 808 for (size_t i = 0; i < InternalChildCount(); ++i) |
| 791 text += InternalGetChild(i)->GetInnerText(); | 809 text += InternalGetChild(i)->GetInnerText(); |
| 792 return text; | 810 return text; |
| 793 } | 811 } |
| 794 | 812 |
| 795 int BrowserAccessibility::GetInnerTextLength() const { | |
| 796 return static_cast<int>(GetInnerText().size()); | |
| 797 } | |
| 798 | |
| 799 void BrowserAccessibility::FixEmptyBounds(gfx::Rect* bounds) const | 813 void BrowserAccessibility::FixEmptyBounds(gfx::Rect* bounds) const |
| 800 { | 814 { |
| 801 if (bounds->width() > 0 && bounds->height() > 0) | 815 if (bounds->width() > 0 && bounds->height() > 0) |
| 802 return; | 816 return; |
| 803 | 817 |
| 804 for (size_t i = 0; i < InternalChildCount(); ++i) { | 818 for (size_t i = 0; i < InternalChildCount(); ++i) { |
| 805 // Compute the bounds of each child - this calls FixEmptyBounds | 819 // Compute the bounds of each child - this calls FixEmptyBounds |
| 806 // recursively if necessary. | 820 // recursively if necessary. |
| 807 BrowserAccessibility* child = InternalGetChild(i); | 821 BrowserAccessibility* child = InternalGetChild(i); |
| 808 gfx::Rect child_bounds = child->GetLocalBoundsRect(); | 822 gfx::Rect child_bounds = child->GetLocalBoundsRect(); |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 856 } | 870 } |
| 857 need_to_offset_web_area = true; | 871 need_to_offset_web_area = true; |
| 858 } | 872 } |
| 859 parent = parent->GetParent(); | 873 parent = parent->GetParent(); |
| 860 } | 874 } |
| 861 | 875 |
| 862 return bounds; | 876 return bounds; |
| 863 } | 877 } |
| 864 | 878 |
| 865 } // namespace content | 879 } // namespace content |
| OLD | NEW |