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