Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(187)

Side by Side Diff: content/browser/accessibility/browser_accessibility.cc

Issue 1598583002: Fixed algorithms that compute bounding rectangles and word start offsets to take into account IA2 h… (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Added a TODO to investigate test failure on Linux. Created 4 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
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
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
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
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
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
OLDNEW
« no previous file with comments | « content/browser/accessibility/browser_accessibility.h ('k') | content/browser/accessibility/browser_accessibility_android.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698