summaryrefslogtreecommitdiff
path: root/Source/WebCore/accessibility/atk/AccessibilityObjectAtk.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/accessibility/atk/AccessibilityObjectAtk.cpp')
-rw-r--r--Source/WebCore/accessibility/atk/AccessibilityObjectAtk.cpp118
1 files changed, 72 insertions, 46 deletions
diff --git a/Source/WebCore/accessibility/atk/AccessibilityObjectAtk.cpp b/Source/WebCore/accessibility/atk/AccessibilityObjectAtk.cpp
index 51e1b787a..238248430 100644
--- a/Source/WebCore/accessibility/atk/AccessibilityObjectAtk.cpp
+++ b/Source/WebCore/accessibility/atk/AccessibilityObjectAtk.cpp
@@ -21,9 +21,13 @@
#include "config.h"
#include "AccessibilityObject.h"
-#include "HTMLNames.h"
-#include "RenderObject.h"
+#include "HTMLSpanElement.h"
+#include "RenderBlock.h"
+#include "RenderInline.h"
+#include "RenderIterator.h"
+#include "RenderTableCell.h"
#include "RenderText.h"
+#include "TextControlInnerElements.h"
#include <glib-object.h>
#if HAVE(ACCESSIBILITY)
@@ -41,10 +45,11 @@ AccessibilityObjectInclusion AccessibilityObject::accessibilityPlatformIncludesO
if (!parent)
return DefaultBehavior;
- AccessibilityRole role = roleValue();
- if (role == HorizontalRuleRole)
- return IncludeObject;
+ // If the author has provided a role, platform-specific inclusion likely doesn't apply.
+ if (ariaRoleAttribute() != UnknownRole)
+ return DefaultBehavior;
+ AccessibilityRole role = roleValue();
// We expose the slider as a whole but not its value indicator.
if (role == SliderThumbRole)
return IgnoreObject;
@@ -59,12 +64,13 @@ AccessibilityObjectInclusion AccessibilityObject::accessibilityPlatformIncludesO
return IgnoreObject;
// Include all tables, even layout tables. The AT can decide what to do with each.
- if (role == CellRole || role == TableRole)
+ if (role == CellRole || role == TableRole || role == ColumnHeaderRole || role == RowHeaderRole)
return IncludeObject;
// The object containing the text should implement AtkText itself.
+ // However, WebCore also maps ARIA's "text" role to the StaticTextRole.
if (role == StaticTextRole)
- return IgnoreObject;
+ return ariaRoleAttribute() != UnknownRole ? DefaultBehavior : IgnoreObject;
// Include all list items, regardless they have or not inline children
if (role == ListItemRole)
@@ -79,52 +85,72 @@ AccessibilityObjectInclusion AccessibilityObject::accessibilityPlatformIncludesO
if (role == UnknownRole)
return IgnoreObject;
+ if (role == InlineRole)
+ return IncludeObject;
+
// Lines past this point only make sense for AccessibilityRenderObjects.
RenderObject* renderObject = renderer();
if (!renderObject)
return DefaultBehavior;
+ // We always want to include paragraphs that have rendered content.
+ // WebCore Accessibility does so unless there is a RenderBlock child.
+ if (role == ParagraphRole) {
+ auto child = childrenOfType<RenderBlock>(downcast<RenderElement>(*renderObject)).first();
+ return child ? IncludeObject : DefaultBehavior;
+ }
+
+ // We always want to include table cells (layout and CSS) that have rendered text content.
+ if (is<RenderTableCell>(renderObject)) {
+ for (const auto& child : childrenOfType<RenderObject>(downcast<RenderElement>(*renderObject))) {
+ if (is<RenderInline>(child) || is<RenderText>(child) || is<HTMLSpanElement>(child.node()))
+ return IncludeObject;
+ }
+ return DefaultBehavior;
+ }
+
+ if (renderObject->isAnonymousBlock()) {
+ // The text displayed by an ARIA menu item is exposed through the accessible name.
+ if (parent->isMenuItem())
+ return IgnoreObject;
+
+ // The text displayed in headings is typically exposed in the heading itself.
+ if (parent->isHeading())
+ return IgnoreObject;
+
+ // The text displayed in list items is typically exposed in the list item itself.
+ if (parent->isListItem())
+ return IgnoreObject;
+
+ // The text displayed in links is typically exposed in the link itself.
+ if (parent->isLink())
+ return IgnoreObject;
+
+ // FIXME: This next one needs some further consideration. But paragraphs are not
+ // typically huge (like divs). And ignoring anonymous block children of paragraphs
+ // will preserve existing behavior.
+ if (parent->roleValue() == ParagraphRole)
+ return IgnoreObject;
+
+ return DefaultBehavior;
+ }
+
+ Node* node = renderObject->node();
+ if (!node)
+ return DefaultBehavior;
+
// We don't want <span> elements to show up in the accessibility hierarchy unless
// we have good reasons for that (e.g. focusable or visible because of containing
// a meaningful accessible name, maybe set through ARIA), so we can use
// atk_component_grab_focus() to set the focus to it.
- Node* node = renderObject->node();
- if (node && node->hasTagName(HTMLNames::spanTag) && !canSetFocusAttribute() && !hasAttributesRequiredForInclusion())
+ if (is<HTMLSpanElement>(node) && !canSetFocusAttribute() && !hasAttributesRequiredForInclusion() && !supportsARIAAttributes())
return IgnoreObject;
- // Given a paragraph or div containing a non-nested anonymous block, WebCore
- // ignores the paragraph or div and includes the block. We want the opposite:
- // ATs are expecting accessible objects associated with textual elements. They
- // usually have no need for the anonymous block. And when the wrong objects
- // get included or ignored, needed accessibility signals do not get emitted.
- if (role == ParagraphRole || role == DivRole) {
- // Don't call textUnderElement() here, because it's slow and it can
- // crash when called while we're in the middle of a subtree being deleted.
- if (!renderObject->firstChildSlow())
- return DefaultBehavior;
-
- if (!parent->renderer() || parent->renderer()->isAnonymousBlock())
- return DefaultBehavior;
-
- for (RenderObject* r = renderObject->firstChildSlow(); r; r = r->nextSibling()) {
- if (r->isAnonymousBlock())
- return IncludeObject;
- }
- }
-
- // Block spans result in objects of ATK_ROLE_PANEL which are almost always unwanted.
- // However, if we ignore block spans whose parent is the body, the child controls
- // will become immediate children of the ATK_ROLE_DOCUMENT_FRAME and any text will
- // become text within the document frame itself. This ultimately may be what we want
- // and would largely be consistent with what we see from Gecko. However, ignoring
- // spans whose parent is the body changes the current behavior we see from WebCore.
- // Until we have sufficient time to properly analyze these cases, we will defer to
- // WebCore. We only check that the parent is not aria because we do not expect
- // anonymous blocks which are aria-related to themselves have an aria role, nor
- // have we encountered instances where the parent of an anonymous block also lacked
- // an aria role but the grandparent had one.
- if (renderObject && renderObject->isAnonymousBlock() && !parent->renderer()->isBody()
- && parent->ariaRoleAttribute() == UnknownRole)
+ // If we include TextControlInnerTextElement children, changes to those children
+ // will result in focus and text notifications that suggest the user is no longer
+ // in the control. This can be especially problematic for screen reader users with
+ // key echo enabled when typing in a password input.
+ if (is<TextControlInnerTextElement>(node))
return IgnoreObject;
return DefaultBehavior;
@@ -157,7 +183,7 @@ bool AccessibilityObject::allowsTextRanges() const
// Check roles as the last fallback mechanism.
AccessibilityRole role = roleValue();
- return role == ParagraphRole || role == LabelRole || role == DivRole || role == FormRole;
+ return role == ParagraphRole || role == LabelRole || role == DivRole || role == FormRole || role == PreRole;
}
unsigned AccessibilityObject::getLengthForTextRange() const
@@ -169,9 +195,9 @@ unsigned AccessibilityObject::getLengthForTextRange() const
// Gtk ATs need this for all text objects; not just text controls.
Node* node = this->node();
- RenderObject* renderer = node ? node->renderer() : 0;
- if (renderer && renderer->isText())
- textLength = toRenderText(*renderer).textLength();
+ RenderObject* renderer = node ? node->renderer() : nullptr;
+ if (is<RenderText>(renderer))
+ textLength = downcast<RenderText>(*renderer).textLength();
// Get the text length from the elements under the
// accessibility object if the value is still zero.