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 |