diff options
Diffstat (limited to 'chromium/content/browser/accessibility/browser_accessibility.cc')
-rw-r--r-- | chromium/content/browser/accessibility/browser_accessibility.cc | 263 |
1 files changed, 185 insertions, 78 deletions
diff --git a/chromium/content/browser/accessibility/browser_accessibility.cc b/chromium/content/browser/accessibility/browser_accessibility.cc index e0827da7792..3501f5c90db 100644 --- a/chromium/content/browser/accessibility/browser_accessibility.cc +++ b/chromium/content/browser/accessibility/browser_accessibility.cc @@ -4,6 +4,8 @@ #include "content/browser/accessibility/browser_accessibility.h" +#include <stddef.h> + #include <algorithm> #include "base/logging.h" @@ -16,12 +18,7 @@ namespace content { -#if !defined(OS_WIN) && \ - !defined(OS_MACOSX) && \ - !defined(OS_ANDROID) && \ - !(defined(OS_LINUX) && !defined(OS_CHROMEOS) && defined(USE_X11)) -// We have subclassess of BrowserAccessibility on some platforms. -// On other platforms, instantiate the base class. +#if !defined(PLATFORM_HAS_NATIVE_ACCESSIBILITY_IMPL) // static BrowserAccessibility* BrowserAccessibility::Create() { return new BrowserAccessibility(); @@ -46,22 +43,32 @@ bool BrowserAccessibility::PlatformIsLeaf() const { if (InternalChildCount() == 0) return true; - // All of these roles may have children that we use as internal - // implementation details, but we want to expose them as leaves - // to platform accessibility APIs. + // These types of objects may have children that we use as internal + // implementation details, but we want to expose them as leaves to platform + // accessibility APIs because screen readers might be confused if they find + // any children. + if (IsSimpleTextControl() || IsTextOnlyObject()) + return true; + + // Roles whose children are only presentational according to the ARIA and + // HTML5 Specs should be hidden from screen readers. + // (Note that whilst ARIA buttons can have only presentational children, HTML5 + // buttons are allowed to have content.) switch (GetRole()) { - case ui::AX_ROLE_COMBO_BOX: - case ui::AX_ROLE_LINE_BREAK: + case ui::AX_ROLE_IMAGE: + case ui::AX_ROLE_MATH: + case ui::AX_ROLE_METER: + case ui::AX_ROLE_SCROLL_BAR: case ui::AX_ROLE_SLIDER: - case ui::AX_ROLE_STATIC_TEXT: - case ui::AX_ROLE_TEXT_FIELD: + case ui::AX_ROLE_SPLITTER: + case ui::AX_ROLE_PROGRESS_INDICATOR: return true; default: return false; } } -uint32 BrowserAccessibility::PlatformChildCount() const { +uint32_t BrowserAccessibility::PlatformChildCount() const { if (HasIntAttribute(ui::AX_ATTR_CHILD_TREE_ID)) { BrowserAccessibilityManager* child_manager = BrowserAccessibilityManager::FromID( @@ -92,12 +99,13 @@ bool BrowserAccessibility::IsDescendantOf( bool BrowserAccessibility::IsTextOnlyObject() const { return GetRole() == ui::AX_ROLE_STATIC_TEXT || - GetRole() == ui::AX_ROLE_LINE_BREAK; + GetRole() == ui::AX_ROLE_LINE_BREAK || + GetRole() == ui::AX_ROLE_INLINE_TEXT_BOX; } BrowserAccessibility* BrowserAccessibility::PlatformGetChild( - uint32 child_index) const { - DCHECK(child_index < PlatformChildCount()); + uint32_t child_index) const { + DCHECK_LT(child_index, PlatformChildCount()); BrowserAccessibility* result = nullptr; if (HasIntAttribute(ui::AX_ATTR_CHILD_TREE_ID)) { @@ -114,11 +122,11 @@ BrowserAccessibility* BrowserAccessibility::PlatformGetChild( } bool BrowserAccessibility::PlatformIsChildOfLeaf() const { - BrowserAccessibility* ancestor = GetParent(); + BrowserAccessibility* ancestor = InternalGetParent(); while (ancestor) { if (ancestor->PlatformIsLeaf()) return true; - ancestor = ancestor->GetParent(); + ancestor = ancestor->InternalGetParent(); } return false; @@ -166,17 +174,20 @@ BrowserAccessibility* BrowserAccessibility::PlatformDeepestLastChild() const { return deepest_child; } -uint32 BrowserAccessibility::InternalChildCount() const { +uint32_t BrowserAccessibility::InternalChildCount() const { if (!node_ || !manager_) return 0; - return static_cast<uint32>(node_->child_count()); + return static_cast<uint32_t>(node_->child_count()); } BrowserAccessibility* BrowserAccessibility::InternalGetChild( - uint32 child_index) const { - if (!node_ || !manager_) - return NULL; - return manager_->GetFromAXNode(node_->ChildAtIndex(child_index)); + uint32_t child_index) const { + if (!node_ || !manager_ || child_index >= InternalChildCount()) + return nullptr; + + const auto child_node = node_->ChildAtIndex(child_index); + DCHECK(child_node); + return manager_->GetFromAXNode(child_node); } BrowserAccessibility* BrowserAccessibility::GetParent() const { @@ -189,11 +200,21 @@ BrowserAccessibility* BrowserAccessibility::GetParent() const { return manager_->GetParentNodeFromParentTree(); } -int32 BrowserAccessibility::GetIndexInParent() const { +BrowserAccessibility* BrowserAccessibility::InternalGetParent() const { + if (!node_ || !manager_) + return nullptr; + ui::AXNode* parent = node_->parent(); + if (parent) + return manager_->GetFromAXNode(parent); + + return nullptr; +} + +int32_t BrowserAccessibility::GetIndexInParent() const { return node_ ? node_->index_in_parent() : -1; } -int32 BrowserAccessibility::GetId() const { +int32_t BrowserAccessibility::GetId() const { return node_ ? node_->id() : -1; } @@ -209,11 +230,11 @@ gfx::Rect BrowserAccessibility::GetLocation() const { return GetData().location; } -int32 BrowserAccessibility::GetRole() const { +int32_t BrowserAccessibility::GetRole() const { return GetData().role; } -int32 BrowserAccessibility::GetState() const { +int32_t BrowserAccessibility::GetState() const { return GetData().state; } @@ -240,20 +261,38 @@ gfx::Rect BrowserAccessibility::GetGlobalBoundsRect() const { gfx::Rect BrowserAccessibility::GetLocalBoundsForRange(int start, int len) const { + DCHECK_GE(start, 0); + DCHECK_GE(len, 0); + + // Standard text fields such as textarea have an embedded div inside them that + // holds all the text. + if (IsSimpleTextControl() && InternalChildCount() == 1) + return InternalGetChild(0)->GetLocalBoundsForRange(start, len); + if (GetRole() != ui::AX_ROLE_STATIC_TEXT) { - // Apply recursively to all static text descendants. For example, if - // you call it on a div with two text node children, it just calls - // GetLocalBoundsForRange on each of the two children (adjusting - // |start| for each one) and unions the resulting rects. gfx::Rect bounds; - for (size_t i = 0; i < InternalChildCount(); ++i) { + for (size_t i = 0; i < InternalChildCount() && len > 0; ++i) { BrowserAccessibility* child = InternalGetChild(i); - int child_len = child->GetStaticTextLenRecursive(); - if (start < child_len && start + len > 0) { - gfx::Rect child_rect = child->GetLocalBoundsForRange(start, len); + // Child objects are of length one, since they are represented by a single + // embedded object character. The exception is text-only objects. + int child_length_in_parent = 1; + if (child->IsTextOnlyObject()) + child_length_in_parent = static_cast<int>(child->GetText().size()); + if (start < child_length_in_parent) { + gfx::Rect child_rect; + if (child->IsTextOnlyObject()) { + child_rect = child->GetLocalBoundsForRange(start, len); + } else { + child_rect = child->GetLocalBoundsForRange( + 0, static_cast<int>(child->GetText().size())); + } bounds.Union(child_rect); + len -= (child_length_in_parent - start); } - start -= child_len; + if (start > child_length_in_parent) + start -= child_length_in_parent; + else + start = 0; } return ElementBoundsToLocalBounds(bounds); } @@ -261,7 +300,6 @@ gfx::Rect BrowserAccessibility::GetLocalBoundsForRange(int start, int len) int end = start + len; int child_start = 0; int child_end = 0; - gfx::Rect bounds; for (size_t i = 0; i < InternalChildCount() && child_end < start + len; ++i) { BrowserAccessibility* child = InternalGetChild(i); @@ -271,11 +309,9 @@ gfx::Rect BrowserAccessibility::GetLocalBoundsForRange(int start, int len) continue; } - std::string child_text; - child->GetStringAttribute(ui::AX_ATTR_VALUE, &child_text); - int child_len = static_cast<int>(child_text.size()); + int child_length = static_cast<int>(child->GetText().size()); child_start = child_end; - child_end += child_len; + child_end += child_length; if (child_end < start) continue; @@ -289,8 +325,8 @@ gfx::Rect BrowserAccessibility::GetLocalBoundsForRange(int start, int len) gfx::Rect child_rect = child->GetLocation(); int text_direction = child->GetIntAttribute( ui::AX_ATTR_TEXT_DIRECTION); - const std::vector<int32>& character_offsets = child->GetIntListAttribute( - ui::AX_ATTR_CHARACTER_OFFSETS); + const std::vector<int32_t>& character_offsets = + child->GetIntListAttribute(ui::AX_ATTR_CHARACTER_OFFSETS); int start_pixel_offset = local_start > 0 ? character_offsets[local_start - 1] : 0; int end_pixel_offset = @@ -351,11 +387,18 @@ gfx::Rect BrowserAccessibility::GetGlobalBoundsForRange(int start, int len) return bounds; } +base::string16 BrowserAccessibility::GetValue() const { + base::string16 value = GetString16Attribute(ui::AX_ATTR_VALUE); + if (value.empty() && IsSimpleTextControl()) + value = GetInnerText(); + return value; +} + int BrowserAccessibility::GetWordStartBoundary( int start, ui::TextBoundaryDirection direction) const { DCHECK_GE(start, -1); // Special offset that indicates that a word boundary has not been found. - int word_start_not_found = GetStaticTextLenRecursive(); + int word_start_not_found = static_cast<int>(GetText().size()); int word_start = word_start_not_found; switch (GetRole()) { @@ -370,20 +413,18 @@ int BrowserAccessibility::GetWordStartBoundary( child_start = child_end; BrowserAccessibility* child = InternalGetChild(i); DCHECK_EQ(child->GetRole(), ui::AX_ROLE_INLINE_TEXT_BOX); - const std::string& child_text = child->GetStringAttribute( - ui::AX_ATTR_VALUE); - int child_len = static_cast<int>(child_text.size()); + int child_len = static_cast<int>(GetText().size()); child_end += child_len; // End is one past the last character. - const std::vector<int32>& word_starts = child->GetIntListAttribute( - ui::AX_ATTR_WORD_STARTS); + const std::vector<int32_t>& word_starts = + child->GetIntListAttribute(ui::AX_ATTR_WORD_STARTS); if (word_starts.empty()) { word_start = child_end; continue; } int local_start = start - child_start; - std::vector<int32>::const_iterator iter = std::upper_bound( + std::vector<int32_t>::const_iterator iter = std::upper_bound( word_starts.begin(), word_starts.end(), local_start); if (iter != word_starts.end()) { if (direction == ui::FORWARDS_DIRECTION) { @@ -425,22 +466,32 @@ int BrowserAccessibility::GetWordStartBoundary( if (!InternalChildCount()) return word_start_not_found; + const BrowserAccessibility* this_object = this; + // Standard text fields such as textarea have an embedded div inside them + // that should be skipped. + if (IsSimpleTextControl() && InternalChildCount() == 1) { + this_object = InternalGetChild(0); + } int child_start = 0; - for (size_t i = 0; i < InternalChildCount(); ++i) { - BrowserAccessibility* child = InternalGetChild(i); - int child_len = child->GetStaticTextLenRecursive(); - int child_word_start = child->GetWordStartBoundary(start, direction); - if (child_word_start < child_len) { - // We have found a possible word boundary. - word_start = child_start + child_word_start; - } + for (size_t i = 0; i < this_object->InternalChildCount(); ++i) { + BrowserAccessibility* child = this_object->InternalGetChild(i); + // Child objects are of length one, since they are represented by a + // single embedded object character. The exception is text-only objects. + int child_len = 1; + if (child->IsTextOnlyObject()) { + child_len = static_cast<int>(child->GetText().size()); + int child_word_start = child->GetWordStartBoundary(start, direction); + if (child_word_start < child_len) { + // We have found a possible word boundary. + word_start = child_start + child_word_start; + } - // Decide when to stop searching. - if ((word_start != word_start_not_found && - direction == ui::FORWARDS_DIRECTION) || - (start < child_len && - direction == ui::BACKWARDS_DIRECTION)) { - break; + // Decide when to stop searching. + if ((word_start != word_start_not_found && + direction == ui::FORWARDS_DIRECTION) || + (start < child_len && direction == ui::BACKWARDS_DIRECTION)) { + break; + } } child_start += child_len; @@ -586,14 +637,14 @@ bool BrowserAccessibility::HasIntListAttribute( return GetData().HasIntListAttribute(attribute); } -const std::vector<int32>& BrowserAccessibility::GetIntListAttribute( +const std::vector<int32_t>& BrowserAccessibility::GetIntListAttribute( ui::AXIntListAttribute attribute) const { return GetData().GetIntListAttribute(attribute); } bool BrowserAccessibility::GetIntListAttribute( ui::AXIntListAttribute attribute, - std::vector<int32>* value) const { + std::vector<int32_t>* value) const { return GetData().GetIntListAttribute(attribute, value); } @@ -629,7 +680,11 @@ bool BrowserAccessibility::GetAriaTristate( if (base::EqualsASCII(value, "mixed")) *is_mixed = true; - return false; // Not set + return false; // Not set. +} + +base::string16 BrowserAccessibility::GetText() const { + return GetInnerText(); } bool BrowserAccessibility::HasState(ui::AXState state_enum) const { @@ -642,8 +697,21 @@ bool BrowserAccessibility::IsCellOrTableHeaderRole() const { GetRole() == ui::AX_ROLE_ROW_HEADER); } -bool BrowserAccessibility::IsEditableText() const { - return HasState(ui::AX_STATE_EDITABLE); +bool BrowserAccessibility::HasCaret() const { + if (HasState(ui::AX_STATE_EDITABLE) && + !HasState(ui::AX_STATE_RICHLY_EDITABLE) && + HasIntAttribute(ui::AX_ATTR_TEXT_SEL_START) && + HasIntAttribute(ui::AX_ATTR_TEXT_SEL_END)) { + return true; + } + + // The caret is always at the focus of the selection. + int32_t focus_id = manager()->GetTreeData().sel_focus_object_id; + BrowserAccessibility* focus_object = manager()->GetFromID(focus_id); + if (!focus_object) + return false; + + return focus_object->IsDescendantOf(this); } bool BrowserAccessibility::IsWebAreaForPresentationalIframe() const { @@ -695,16 +763,55 @@ bool BrowserAccessibility::IsControl() const { } } -int BrowserAccessibility::GetStaticTextLenRecursive() const { - if (GetRole() == ui::AX_ROLE_STATIC_TEXT || - GetRole() == ui::AX_ROLE_LINE_BREAK) { - return static_cast<int>(GetStringAttribute(ui::AX_ATTR_VALUE).size()); +bool BrowserAccessibility::IsSimpleTextControl() const { + // Time fields, color wells and spinner buttons might also use text fields as + // constituent parts, but they are not considered text fields as a whole. + switch (GetRole()) { + case ui::AX_ROLE_COMBO_BOX: + case ui::AX_ROLE_SEARCH_BOX: + case ui::AX_ROLE_TEXT_FIELD: + return true; + default: + return false; + } +} + +// Indicates if this object is at the root of a rich edit text control. +bool BrowserAccessibility::IsRichTextControl() const { + return HasState(ui::AX_STATE_RICHLY_EDITABLE) && + (!GetParent() || !GetParent()->HasState(ui::AX_STATE_RICHLY_EDITABLE)); +} + +std::string BrowserAccessibility::ComputeAccessibleNameFromDescendants() { + std::string name; + for (size_t i = 0; i < InternalChildCount(); ++i) { + BrowserAccessibility* child = InternalGetChild(i); + std::string child_name; + if (child->GetStringAttribute(ui::AX_ATTR_NAME, &child_name)) { + if (!name.empty()) + name += " "; + name += child_name; + } else if (!child->HasState(ui::AX_STATE_FOCUSABLE)) { + child_name = child->ComputeAccessibleNameFromDescendants(); + if (!child_name.empty()) { + if (!name.empty()) + name += " "; + name += child_name; + } + } } - int len = 0; + return name; +} + +base::string16 BrowserAccessibility::GetInnerText() const { + if (IsTextOnlyObject()) + return GetString16Attribute(ui::AX_ATTR_NAME); + + base::string16 text; for (size_t i = 0; i < InternalChildCount(); ++i) - len += InternalGetChild(i)->GetStaticTextLenRecursive(); - return len; + text += InternalGetChild(i)->GetInnerText(); + return text; } void BrowserAccessibility::FixEmptyBounds(gfx::Rect* bounds) const |