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

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

Issue 1130733006: Adds color, font size, text direction and text styles to the accessibility tree. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: First attempt at exposing style info to the native APIs. Created 5 years, 7 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 <algorithm> 7 #include <algorithm>
8 8
9 #include "base/logging.h" 9 #include "base/logging.h"
10 #include "base/strings/string_number_conversions.h" 10 #include "base/strings/string_number_conversions.h"
(...skipping 244 matching lines...) Expand 10 before | Expand all | Expand 10 after
255 const std::vector<int32>& character_offsets = child->GetIntListAttribute( 255 const std::vector<int32>& character_offsets = child->GetIntListAttribute(
256 ui::AX_ATTR_CHARACTER_OFFSETS); 256 ui::AX_ATTR_CHARACTER_OFFSETS);
257 int start_pixel_offset = 257 int start_pixel_offset =
258 local_start > 0 ? character_offsets[local_start - 1] : 0; 258 local_start > 0 ? character_offsets[local_start - 1] : 0;
259 int end_pixel_offset = 259 int end_pixel_offset =
260 local_end > 0 ? character_offsets[local_end - 1] : 0; 260 local_end > 0 ? character_offsets[local_end - 1] : 0;
261 261
262 gfx::Rect child_overlap_rect; 262 gfx::Rect child_overlap_rect;
263 switch (text_direction) { 263 switch (text_direction) {
264 case ui::AX_TEXT_DIRECTION_NONE: 264 case ui::AX_TEXT_DIRECTION_NONE:
265 case ui::AX_TEXT_DIRECTION_LR: { 265 case ui::AX_TEXT_DIRECTION_LTR: {
266 int left = child_rect.x() + start_pixel_offset; 266 int left = child_rect.x() + start_pixel_offset;
267 int right = child_rect.x() + end_pixel_offset; 267 int right = child_rect.x() + end_pixel_offset;
268 child_overlap_rect = gfx::Rect(left, child_rect.y(), 268 child_overlap_rect = gfx::Rect(left, child_rect.y(),
269 right - left, child_rect.height()); 269 right - left, child_rect.height());
270 break; 270 break;
271 } 271 }
272 case ui::AX_TEXT_DIRECTION_RL: { 272 case ui::AX_TEXT_DIRECTION_RTL: {
273 int right = child_rect.right() - start_pixel_offset; 273 int right = child_rect.right() - start_pixel_offset;
274 int left = child_rect.right() - end_pixel_offset; 274 int left = child_rect.right() - end_pixel_offset;
275 child_overlap_rect = gfx::Rect(left, child_rect.y(), 275 child_overlap_rect = gfx::Rect(left, child_rect.y(),
276 right - left, child_rect.height()); 276 right - left, child_rect.height());
277 break; 277 break;
278 } 278 }
279 case ui::AX_TEXT_DIRECTION_TB: { 279 case ui::AX_TEXT_DIRECTION_TTB: {
280 int top = child_rect.y() + start_pixel_offset; 280 int top = child_rect.y() + start_pixel_offset;
281 int bottom = child_rect.y() + end_pixel_offset; 281 int bottom = child_rect.y() + end_pixel_offset;
282 child_overlap_rect = gfx::Rect(child_rect.x(), top, 282 child_overlap_rect = gfx::Rect(child_rect.x(), top,
283 child_rect.width(), bottom - top); 283 child_rect.width(), bottom - top);
284 break; 284 break;
285 } 285 }
286 case ui::AX_TEXT_DIRECTION_BT: { 286 case ui::AX_TEXT_DIRECTION_BTT: {
287 int bottom = child_rect.bottom() - start_pixel_offset; 287 int bottom = child_rect.bottom() - start_pixel_offset;
288 int top = child_rect.bottom() - end_pixel_offset; 288 int top = child_rect.bottom() - end_pixel_offset;
289 child_overlap_rect = gfx::Rect(child_rect.x(), top, 289 child_overlap_rect = gfx::Rect(child_rect.x(), top,
290 child_rect.width(), bottom - top); 290 child_rect.width(), bottom - top);
291 break; 291 break;
292 } 292 }
293 default: 293 default:
294 NOTREACHED(); 294 NOTREACHED();
295 } 295 }
296 296
(...skipping 11 matching lines...) Expand all
308 gfx::Rect bounds = GetLocalBoundsForRange(start, len); 308 gfx::Rect bounds = GetLocalBoundsForRange(start, len);
309 309
310 // Adjust the bounds by the top left corner of the containing view's bounds 310 // Adjust the bounds by the top left corner of the containing view's bounds
311 // in screen coordinates. 311 // in screen coordinates.
312 bounds.Offset(manager_->GetViewBounds().OffsetFromOrigin()); 312 bounds.Offset(manager_->GetViewBounds().OffsetFromOrigin());
313 313
314 return bounds; 314 return bounds;
315 } 315 }
316 316
317 int BrowserAccessibility::GetWordStartBoundary( 317 int BrowserAccessibility::GetWordStartBoundary(
318 int start, ui::TextBoundaryDirection direction) const { 318 int offset, ui::TextBoundaryDirection direction) const {
319 DCHECK_GE(start, -1); 319 DCHECK_GE(offset, -1);
320 int text_len = GetStaticTextLenRecursive();
321 DCHECK_LE(offset, text_len);
322
320 // Special offset that indicates that a word boundary has not been found. 323 // Special offset that indicates that a word boundary has not been found.
321 int word_start_not_found = GetStaticTextLenRecursive(); 324 int word_start_not_found = text_len;
322 int word_start = word_start_not_found; 325 int word_start = word_start_not_found;
323 326
324 switch (GetRole()) { 327 switch (GetRole()) {
325 case ui::AX_ROLE_STATIC_TEXT: { 328 case ui::AX_ROLE_STATIC_TEXT: {
326 int prev_word_start = word_start_not_found; 329 int prev_word_start = word_start_not_found;
327 int child_start = 0; 330 int child_start = 0;
328 int child_end = 0; 331 int child_end = 0;
329 332
330 // Go through the inline text boxes. 333 // Go through the inline text boxes.
331 for (size_t i = 0; i < InternalChildCount(); ++i) { 334 for (size_t i = 0; i < InternalChildCount(); ++i) {
332 // The next child starts where the previous one ended. 335 // The next child starts where the previous one ended.
333 child_start = child_end; 336 child_start = child_end;
334 BrowserAccessibility* child = InternalGetChild(i); 337 BrowserAccessibility* child = InternalGetChild(i);
335 DCHECK_EQ(child->GetRole(), ui::AX_ROLE_INLINE_TEXT_BOX); 338 if (child->GetRole(), ui::AX_ROLE_INLINE_TEXT_BOX) {
336 const std::string& child_text = child->GetStringAttribute( 339 LOG(WARNING) << "Found child of AX_ROLE_STATIC_TEXT that is not " <<
337 ui::AX_ATTR_VALUE); 340 "AX_ROLE_INLINE_TEXT_BOX. ID=" child->GetId());
338 int child_len = static_cast<int>(child_text.size()); 341 continue;
339 child_end += child_len; // End is one past the last character. 342 }
343 int child_len = GetStaticTextLenRecursive();
344 child_end += child_len;
340 345
341 const std::vector<int32>& word_starts = child->GetIntListAttribute( 346 const std::vector<int32>& word_starts = child->GetIntListAttribute(
342 ui::AX_ATTR_WORD_STARTS); 347 ui::AX_ATTR_WORD_STARTS);
343 if (word_starts.empty()) { 348 if (word_starts.empty()) {
344 word_start = child_end; 349 word_start = child_end;
345 continue; 350 continue;
346 } 351 }
347 352
348 int local_start = start - child_start; 353 int local_offset = offset - child_start;
349 std::vector<int32>::const_iterator iter = std::upper_bound( 354 std::vector<int32>::const_iterator iter = std::upper_bound(
350 word_starts.begin(), word_starts.end(), local_start); 355 word_starts.begin(), word_starts.end(), local_offset);
351 if (iter != word_starts.end()) { 356 if (iter != word_starts.end()) {
352 if (direction == ui::FORWARDS_DIRECTION) { 357 if (direction == ui::FORWARDS_DIRECTION) {
353 word_start = child_start + *iter; 358 word_start = child_start + *iter;
354 } else if (direction == ui::BACKWARDS_DIRECTION) { 359 } else if (direction == ui::BACKWARDS_DIRECTION) {
355 if (iter == word_starts.begin()) { 360 if (iter == word_starts.begin()) {
356 // Return the position of the last word in the previous child. 361 // Return the position of the last word in the previous child.
357 word_start = prev_word_start; 362 word_start = prev_word_start;
358 } else { 363 } else {
359 word_start = child_start + *(iter - 1); 364 word_start = child_start + *(iter - 1);
360 } 365 }
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
394 int child_len = child->GetStaticTextLenRecursive(); 399 int child_len = child->GetStaticTextLenRecursive();
395 int child_word_start = child->GetWordStartBoundary(start, direction); 400 int child_word_start = child->GetWordStartBoundary(start, direction);
396 if (child_word_start < child_len) { 401 if (child_word_start < child_len) {
397 // We have found a possible word boundary. 402 // We have found a possible word boundary.
398 word_start = child_start + child_word_start; 403 word_start = child_start + child_word_start;
399 } 404 }
400 405
401 // Decide when to stop searching. 406 // Decide when to stop searching.
402 if ((word_start != word_start_not_found && 407 if ((word_start != word_start_not_found &&
403 direction == ui::FORWARDS_DIRECTION) || 408 direction == ui::FORWARDS_DIRECTION) ||
404 (start < child_len && 409 (offset < child_len &&
405 direction == ui::BACKWARDS_DIRECTION)) { 410 direction == ui::BACKWARDS_DIRECTION)) {
406 break; 411 break;
407 } 412 }
408 413
409 child_start += child_len; 414 child_start += child_len;
410 if (start >= child_len) 415 if (offset >= child_len)
411 start -= child_len; 416 offset -= child_len;
412 else 417 else
413 start = -1; 418 offset = -1;
414 } 419 }
415 return word_start; 420 return word_start;
416 } 421 }
417 } 422 }
418 423
424 int BrowserAccessibility::GetStyleChangeBoundary(
425 int offset, ui::TextBoundaryDirection direction) const {
426 DCHECK_GE(offset, 0);
427 int text_len = GetStaticTextLenRecursive();
428 DCHECK_LE(offset, text_len);
429
430 int style_change_start;
431 if (direction == ui::BACKWARDS_DIRECTION)
432 style_change_start = 0;
433 else if (direction == ui::FORWARDS_DIRECTION)
434 style_change_start = text_len;
435 else
436 NOTREACHED();
437
438 if (IsTextLeaf())
439 return style_change_start;
440
441 BrowserAccessibility* leaf = GetTextLeafAtOffset(offset, &style_change_start);
442 DCHECK(leaf);
443
444 BrowserAccessibility* sibling = leaf;
445 if (direction == ui::BACKWARDS_DIRECTION)
446 while ((sibling = GetPreviousTextLeaf()) && HaveSameStyle(*leaf, *sibling)) {
447 style_change_start -= sibling->GetStaticTextLenRecursive();
448 leaf = sibling;
449 }
450 else if (direction == ui::FORWARDS_DIRECTION)
451 while (sibling = GetNextTextLeaf() && HaveSameStyle(*leaf, *sibling)) {
452 style_change_start += leaf->GetStaticTextLenRecursive();
453 leaf = sibling;
454 }
455
456 return style_change_start;
457 }
458
419 BrowserAccessibility* BrowserAccessibility::BrowserAccessibilityForPoint( 459 BrowserAccessibility* BrowserAccessibility::BrowserAccessibilityForPoint(
420 const gfx::Point& point) { 460 const gfx::Point& point) {
421 // The best result found that's a child of this object. 461 // The best result found that's a child of this object.
422 BrowserAccessibility* child_result = NULL; 462 BrowserAccessibility* child_result = NULL;
423 // The best result that's an indirect descendant like grandchild, etc. 463 // The best result that's an indirect descendant like grandchild, etc.
424 BrowserAccessibility* descendant_result = NULL; 464 BrowserAccessibility* descendant_result = NULL;
425 465
426 // Walk the children recursively looking for the BrowserAccessibility that 466 // Walk the children recursively looking for the BrowserAccessibility that
427 // most tightly encloses the specified point. Walk backwards so that in 467 // most tightly encloses the specified point. Walk backwards so that in
428 // the absence of any other information, we assume the object that occurs 468 // the absence of any other information, we assume the object that occurs
(...skipping 323 matching lines...) Expand 10 before | Expand all | Expand 10 after
752 if (!parent) 792 if (!parent)
753 return false; 793 return false;
754 794
755 BrowserAccessibility* grandparent = parent->GetParent(); 795 BrowserAccessibility* grandparent = parent->GetParent();
756 if (!grandparent) 796 if (!grandparent)
757 return false; 797 return false;
758 798
759 return grandparent->GetRole() == ui::AX_ROLE_IFRAME_PRESENTATIONAL; 799 return grandparent->GetRole() == ui::AX_ROLE_IFRAME_PRESENTATIONAL;
760 } 800 }
761 801
802 //
803 // Protected methods.
804 //
805
806 BrowserAccessibility* BrowserAccessibility::GetFirstTextLeaf() const {
807 int unused_leaf_offset;
808 return GetTextLeafAtOffset(0, &unused_leaf_offset);
809 }
810
811 //
812 // Private methods.
813 //
814
815 // Return true only for objects that are the deepest possible descendants which
816 // include complete text information.
817 // For example, objects with role AX_ROLE_EDIT_FIELD are not the deepest nodes
818 // with complete text information because they have objects with role
819 // AX_ROLE_STATIC_TEXT as their descendants which hold the text information.
820 // However, an object with role AX_ROLE_BUTTON has no AX_ROLE_STATIC_TEXT
821 // descendant and it stores its text information directly.
822 bool BrowserAccessibility::IsTextLeaf() const {
823 switch (GetRole()) {
824 case ui::AX_ROLE_BUTTON:
825 case ui::AX_ROLE_CHECK_BOX:
826 case ui::AX_ROLE_COLOR_WELL:
827 case ui::AX_ROLE_COMBO_BOX:
828 case ui::AX_ROLE_DATE:
829 case ui::AX_ROLE_DATE_TIME:
830 case ui::AX_ROLE_IMAGE:
831 case ui::AX_ROLE_LINE_BREAK:
832 case ui::AX_ROLE_LIST_MARKER:
833 case ui::AX_ROLE_POP_UP_BUTTON:
834 case ui::AX_ROLE_RADIO_BUTTON:
835 case ui::AX_ROLE_SEARCH_BOX:
836 case ui::AX_ROLE_TOGGLE_BUTTON:
837 case ui::AX_ROLE_STATIC_TEXT:
838 return true;
839 default:
840 return false;
841 }
842 }
843
762 int BrowserAccessibility::GetStaticTextLenRecursive() const { 844 int BrowserAccessibility::GetStaticTextLenRecursive() const {
763 if (GetRole() == ui::AX_ROLE_STATIC_TEXT || 845 if (IsTextLeaf())
764 GetRole() == ui::AX_ROLE_LINE_BREAK) {
765 return static_cast<int>(GetStringAttribute(ui::AX_ATTR_VALUE).size()); 846 return static_cast<int>(GetStringAttribute(ui::AX_ATTR_VALUE).size());
766 }
767 847
768 int len = 0; 848 int len = 0;
769 for (size_t i = 0; i < InternalChildCount(); ++i) 849 for (size_t i = 0; i < InternalChildCount(); ++i)
770 len += InternalGetChild(i)->GetStaticTextLenRecursive(); 850 len += InternalGetChild(i)->GetStaticTextLenRecursive();
771 return len; 851 return len;
772 } 852 }
773 853
854 // TODO(nektar): Refactor whole class to use this function where appropriate.
855 BrowserAccessibility* BrowserAccessibility::GetTextLeafAtOffset(
856 int offset,
857 int* leaf_start_offset) const {
858 DCHECK_GE(offset, 0);
859 DCHECK_LE(offset, GetStaticTextLenRecursive());
860 DCHECK(child_start_offset);
861
862 if (IsTextLeaf()) {
863 *leaf_start_offset = 0;
864 return this;
865 }
866
867 int child_start = 0;
868 BrowserAccessibility* leaf = nullptr;
869 for (size_t i = 0; i < InternalChildCount(); ++i) {
870 BrowserAccessibility* child = InternalGetChild(i);
871 int child_len = GetStaticTextLenRecursive();
872 int child_end = child_start + child_len;
873 if (offset < child_end) {
874 leaf = child->GetTextLeafAtOffset(offset, leaf_start_offset);
875 break;
876 }
877
878 child_start = child_end;
879 offset -= child_len;
880 }
881
882 *leaf_start_offset += child_start;
883 return leaf;
884 }
885
886 BrowserAccessibility* BrowserAccessibility::GetLastTextLeaf() const {
887 int text_len = GetStaticTextLenRecursive();
888 if (text_len <= 0)
889 return GetFirstTextLeaf();
890
891 int unused_leaf_offset;
892 return GetTextLeafAtOffset(text_len - 1, &unused_leaf_offset);
893 }
894
895 BrowserAccessibility* BrowserAccessibility::GetNextTextLeaf() const {
896 if (GetFirstTextLeaf() && GetFirstTextLeaf() != this)
897 return GetNextFirstLeaf();
898
899 BrowserAccessibility* next_leaf = nullptr;
900 BrowserAccessibility* current_object = GetNextSibling();
901 while (!next_leaf && current_object) {
902 next_leaf = current_object->GetFirstTextLeaf();
903 current_object = current_object->GetNextSibling();
904 }
905 if (next_leaf)
906 return next_leaf;
907
908 if (GetParent() && GetParent()->GetNextSibling())
909 next_leaf = GetParent()->GetNextSibling()->GetNextTextLeaf();
910
911 return next_leaf;
912 }
913
914 BrowserAccessibility* BrowserAccessibility::GetPreviousTextLeaf() const {
915 BrowserAccessibility* previous_leaf = nullptr;
916 BrowserAccessibility* current_object = GetPreviousSibling();
917 while (!previous_leaf && current_object) {
918 previous_leaf = current_object->GetLastTextLeaf();
919 current_object = current_object->GetPreviousSibling();
920 }
921 if (previous_leaf)
922 return previous_leaf;
923
924 if (GetParent() && GetParent()->GetPreviousSibling())
925 previous_leaf = GetParent()->GetPreviousSibling()->GetPreviousTextLeaf();
926
927 return previous_leaf;
928 }
929
774 BrowserAccessibility* BrowserAccessibility::GetParentForBoundsCalculation() 930 BrowserAccessibility* BrowserAccessibility::GetParentForBoundsCalculation()
775 const { 931 const {
776 if (!node_ || !manager_) 932 if (!node_ || !manager_)
777 return NULL; 933 return NULL;
778 ui::AXNode* parent = node_->parent(); 934 ui::AXNode* parent = node_->parent();
779 if (parent) 935 if (parent)
780 return manager_->GetFromAXNode(parent); 936 return manager_->GetFromAXNode(parent);
781 937
782 if (!manager_->delegate()) 938 if (!manager_->delegate())
783 return NULL; 939 return NULL;
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
818 bounds.Offset(-sx, -sy); 974 bounds.Offset(-sx, -sy);
819 } 975 }
820 need_to_offset_web_area = true; 976 need_to_offset_web_area = true;
821 } 977 }
822 parent = parent->GetParentForBoundsCalculation(); 978 parent = parent->GetParentForBoundsCalculation();
823 } 979 }
824 980
825 return bounds; 981 return bounds;
826 } 982 }
827 983
984 //
985 // Static methods.
986 //
987
988 bool BrowserAccessibility::HaveSameStyle(BrowserAccessibility& object1,
989 BrowserAccessibility& object2) {
990
991 }
992
828 } // namespace content 993 } // namespace content
OLDNEW
« no previous file with comments | « content/browser/accessibility/browser_accessibility.h ('k') | content/browser/accessibility/browser_accessibility_android.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698