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

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: Addressed comments by reviewer. 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 86 matching lines...) Expand 10 before | Expand all | Expand 10 after
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
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
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
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
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698