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

Unified Diff: Source/modules/accessibility/AXLayoutObject.cpp

Issue 1076453004: Show reasons why nodes are ignored in accessibility sidebar (Closed) Base URL: https://chromium.googlesource.com/chromium/blink.git@master
Patch Set: rebase Created 5 years, 8 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 side-by-side diff with in-line comments
Download patch
Index: Source/modules/accessibility/AXLayoutObject.cpp
diff --git a/Source/modules/accessibility/AXLayoutObject.cpp b/Source/modules/accessibility/AXLayoutObject.cpp
index 46ead68c75698a1e6654024b5115b0f297c38d9e..67f7e5fefcbfcfa26b54a0a86d0382907fca0192 100644
--- a/Source/modules/accessibility/AXLayoutObject.cpp
+++ b/Source/modules/accessibility/AXLayoutObject.cpp
@@ -71,6 +71,7 @@
#include "modules/accessibility/AXSVGRoot.h"
#include "modules/accessibility/AXSpinButton.h"
#include "modules/accessibility/AXTable.h"
+#include "modules/accessibility/InspectorTypeBuilderHelper.h"
#include "platform/text/PlatformLocale.h"
#include "wtf/StdLibExtras.h"
@@ -80,6 +81,8 @@ namespace blink {
using namespace HTMLNames;
+using TypeBuilder::Accessibility::AXIgnoredReasons;
+
static inline LayoutObject* firstChildInContinuation(const LayoutInline& layoutObject)
{
LayoutBoxModelObject* r = layoutObject.continuation();
@@ -464,52 +467,74 @@ bool AXLayoutObject::isSelected() const
// Whether objects are ignored, i.e. not included in the tree.
//
-AXObjectInclusion AXLayoutObject::defaultObjectInclusion() const
+AXObjectInclusion AXLayoutObject::defaultObjectInclusion(PassRefPtr<TypeBuilder::Array<TypeBuilder::Accessibility::AXProperty>> passIgnoredReasons) const
{
// The following cases can apply to any element that's a subclass of AXLayoutObject.
- if (!m_layoutObject)
+ RefPtr<TypeBuilder::Array<TypeBuilder::Accessibility::AXProperty>> ignoredReasons = passIgnoredReasons;
+
+ if (!m_layoutObject) {
+ if (ignoredReasons)
+ ignoredReasons->addItem(createProperty(AXIgnoredReasons::NotRendered, createBooleanValue(true)));
return IgnoreObject;
+ }
if (m_layoutObject->style()->visibility() != VISIBLE) {
// aria-hidden is meant to override visibility as the determinant in AX hierarchy inclusion.
if (equalIgnoringCase(getAttribute(aria_hiddenAttr), "false"))
return DefaultBehavior;
+ if (ignoredReasons)
+ ignoredReasons->addItem(createProperty(AXIgnoredReasons::NotVisible, createBooleanValue(true)));
return IgnoreObject;
}
- return AXObject::defaultObjectInclusion();
+ return AXObject::defaultObjectInclusion(ignoredReasons);
}
-bool AXLayoutObject::computeAccessibilityIsIgnored() const
+bool AXLayoutObject::computeAccessibilityIsIgnored(PassRefPtr<TypeBuilder::Array<TypeBuilder::Accessibility::AXProperty>> passIgnoredReasons) const
{
#if ENABLE(ASSERT)
ASSERT(m_initialized);
#endif
+ RefPtr<TypeBuilder::Array<TypeBuilder::Accessibility::AXProperty>> ignoredReasons = passIgnoredReasons;
+
// Check first if any of the common reasons cause this element to be ignored.
// Then process other use cases that need to be applied to all the various roles
// that AXLayoutObjects take on.
- AXObjectInclusion decision = defaultObjectInclusion();
+ AXObjectInclusion decision = defaultObjectInclusion(ignoredReasons);
if (decision == IncludeObject)
return false;
if (decision == IgnoreObject)
return true;
- // If this element is within a parent that cannot have children, it should not be exposed.
- if (isDescendantOfBarrenParent())
+ // If this element is within a parent that cannot have children, it should not be exposed
+ if (isDescendantOfLeafNode()) {
+ if (ignoredReasons)
+ ignoredReasons->addItem(createProperty(AXIgnoredReasons::AncestorIsLeafNode, createRelatedNodeValue(leafNodeAncestor())));
dmazzoni 2015/04/15 17:00:02 Perhaps you could add a helper function for this.
aboxhall 2015/04/15 17:55:46 I feel silly for not thinking of this. Done.
aboxhall 2015/04/15 17:55:46 Great idea, done.
return true;
+ }
- if (roleValue() == IgnoredRole)
+ if (roleValue() == IgnoredRole) {
+ if (ignoredReasons) {
+ ignoredReasons->addItem(createProperty(AXIgnoredReasons::Uninteresting, createBooleanValue(true)));
+ }
return true;
+ }
- if (hasInheritedPresentationalRole())
+ if (hasInheritedPresentationalRole()) {
+ if (ignoredReasons)
+ ignoredReasons->addItem(createProperty(AXIgnoredReasons::InheritsPresentation, createRelatedNodeValue(inheritsPresentationalRoleFrom())));
return true;
+ }
// An ARIA tree can only have tree items and static text as children.
- if (!isAllowedChildOfTree())
+ if (AXObject* treeAncestor = treeAncestorDisallowingChild()) {
+ if (ignoredReasons)
+ ignoredReasons->addItem(createProperty(AXIgnoredReasons::AncestorDisallowsChild, createRelatedNodeValue(treeAncestor)));
return true;
+ }
// TODO: we should refactor this - but right now this is necessary to make
// sure scroll areas stay in the tree.
@@ -518,28 +543,49 @@ bool AXLayoutObject::computeAccessibilityIsIgnored() const
// ignore popup menu items because AppKit does
for (LayoutObject* parent = m_layoutObject->parent(); parent; parent = parent->parent()) {
- if (parent->isBoxModelObject() && toLayoutBoxModelObject(parent)->isMenuList())
+ if (parent->isBoxModelObject() && toLayoutBoxModelObject(parent)->isMenuList()) {
+ if (ignoredReasons) {
+ AXObject* parentObject = axObjectCache()->getOrCreate(parent);
+ if (parentObject)
+ ignoredReasons->addItem(createProperty(AXIgnoredReasons::AncestorDisallowsChild, createRelatedNodeValue(parentObject)));
+ }
return true;
+ }
}
// find out if this element is inside of a label element.
// if so, it may be ignored because it's the label for a checkbox or radio button
AXObject* controlObject = correspondingControlForLabelElement();
- if (controlObject && !controlObject->deprecatedExposesTitleUIElement() && controlObject->isCheckboxOrRadio())
+ if (controlObject && !controlObject->deprecatedExposesTitleUIElement() && controlObject->isCheckboxOrRadio()) {
+ if (ignoredReasons) {
+ HTMLLabelElement* label = labelElementContainer();
+ if (label && !label->isSameNode(node())) {
+ AXObject* labelAXObject = axObjectCache()->getOrCreate(label);
+ ignoredReasons->addItem(createProperty(AXIgnoredReasons::LabelContainer, createRelatedNodeValue(labelAXObject)));
+ }
+
+ ignoredReasons->addItem(createProperty(AXIgnoredReasons::LabelFor, createRelatedNodeValue(controlObject)));
+ }
return true;
+ }
if (m_layoutObject->isBR())
return false;
- // NOTE: BRs always have text boxes now, so the text box check here can be removed
if (m_layoutObject->isText()) {
// static text beneath MenuItems and MenuButtons are just reported along with the menu item, so it's ignored on an individual level
AXObject* parent = parentObjectUnignored();
- if (parent && (parent->ariaRoleAttribute() == MenuItemRole || parent->ariaRoleAttribute() == MenuButtonRole))
+ if (parent && (parent->ariaRoleAttribute() == MenuItemRole || parent->ariaRoleAttribute() == MenuButtonRole)) {
+ if (ignoredReasons)
+ ignoredReasons->addItem(createProperty(AXIgnoredReasons::StaticTextUsedAsNameFor, createRelatedNodeValue(parent)));
return true;
+ }
LayoutText* layoutText = toLayoutText(m_layoutObject);
- if (m_layoutObject->isBR() || !layoutText->firstTextBox())
+ if (!layoutText->firstTextBox()) {
+ if (ignoredReasons)
+ ignoredReasons->addItem(createProperty(AXIgnoredReasons::EmptyText, createBooleanValue(true)));
return true;
+ }
// Don't ignore static text in editable text controls.
for (AXObject* parent = parentObject(); parent; parent = parent->parentObject()) {
@@ -549,7 +595,12 @@ bool AXLayoutObject::computeAccessibilityIsIgnored() const
// text elements that are just empty whitespace should not be returned
// FIXME(dmazzoni): we probably shouldn't ignore this if the style is 'pre', or similar...
- return layoutText->text().impl()->containsOnlyWhitespace();
+ if (layoutText->text().impl()->containsOnlyWhitespace()) {
+ if (ignoredReasons)
+ ignoredReasons->addItem(createProperty(AXIgnoredReasons::EmptyText, createBooleanValue(true)));
+ return true;
+ }
+ return false;
}
if (isHeading())
@@ -617,11 +668,20 @@ bool AXLayoutObject::computeAccessibilityIsIgnored() const
// objects are often containers with meaningful information, the inclusion of a span can have
// the side effect of causing the immediate parent accessible to be ignored. This is especially
// problematic for platforms which have distinct roles for textual block elements.
- if (isHTMLSpanElement(node))
+ if (isHTMLSpanElement(node)) {
+ if (ignoredReasons)
+ ignoredReasons->addItem(createProperty(AXIgnoredReasons::Uninteresting, createBooleanValue(true)));
return true;
+ }
- if (m_layoutObject->isLayoutBlockFlow() && m_layoutObject->childrenInline() && !canSetFocusAttribute())
- return !toLayoutBlockFlow(m_layoutObject)->firstLineBox() && !mouseButtonListener();
+ if (m_layoutObject->isLayoutBlockFlow() && m_layoutObject->childrenInline() && !canSetFocusAttribute()) {
+ if (toLayoutBlockFlow(m_layoutObject)->firstLineBox() || mouseButtonListener())
+ return false;
+
+ if (ignoredReasons)
+ ignoredReasons->addItem(createProperty(AXIgnoredReasons::Uninteresting, createBooleanValue(true)));
+ return true;
+ }
// ignore images seemingly used as spacers
if (isImage()) {
@@ -637,20 +697,31 @@ bool AXLayoutObject::computeAccessibilityIsIgnored() const
if (!alt.string().containsOnlyWhitespace())
return false;
// informal standard is to ignore images with zero-length alt strings
- if (!alt.isNull())
+ if (!alt.isNull()) {
+ if (ignoredReasons)
+ ignoredReasons->addItem(createProperty(AXIgnoredReasons::EmptyAlt, createBooleanValue(true)));
return true;
+ }
}
if (isNativeImage() && m_layoutObject->isImage()) {
// check for one-dimensional image
LayoutImage* image = toLayoutImage(m_layoutObject);
- if (image->size().height() <= 1 || image->size().width() <= 1)
+ if (image->size().height() <= 1 || image->size().width() <= 1) {
+ if (ignoredReasons)
+ ignoredReasons->addItem(createProperty(AXIgnoredReasons::ProbablyPresentational, createBooleanValue(true)));
return true;
+ }
// check whether laid out image was stretched from one-dimensional file image
if (image->cachedImage()) {
LayoutSize imageSize = image->cachedImage()->imageSizeForLayoutObject(m_layoutObject, image->view()->zoomFactor());
- return imageSize.height() <= 1 || imageSize.width() <= 1;
+ if (imageSize.height() <= 1 || imageSize.width() <= 1) {
+ if (ignoredReasons)
+ ignoredReasons->addItem(createProperty(AXIgnoredReasons::ProbablyPresentational, createBooleanValue(true)));
+ return true;
+ }
+ return false;
}
}
return false;
@@ -660,8 +731,11 @@ bool AXLayoutObject::computeAccessibilityIsIgnored() const
if (canvasHasFallbackContent())
return false;
LayoutHTMLCanvas* canvas = toLayoutHTMLCanvas(m_layoutObject);
- if (canvas->size().height() <= 1 || canvas->size().width() <= 1)
+ if (canvas->size().height() <= 1 || canvas->size().width() <= 1) {
+ if (ignoredReasons)
+ ignoredReasons->addItem(createProperty(AXIgnoredReasons::ProbablyPresentational, createBooleanValue(true)));
return true;
+ }
// Otherwise fall through; use presence of help text, title, or description to decide.
}
@@ -689,6 +763,8 @@ bool AXLayoutObject::computeAccessibilityIsIgnored() const
// By default, objects should be ignored so that the AX hierarchy is not
// filled with unnecessary items.
+ if (ignoredReasons)
+ ignoredReasons->addItem(createProperty(AXIgnoredReasons::Uninteresting, createBooleanValue(true)));
return true;
}
@@ -1012,7 +1088,7 @@ bool AXLayoutObject::ariaRoleHasPresentationalChildren() const
}
}
-bool AXLayoutObject::isPresentationalChildOfAriaRole() const
+AXObject* AXLayoutObject::ancestorForWhichThisIsAPresentationalChild() const
{
// Walk the parent chain looking for a parent that has presentational children
AXObject* parent;
@@ -1841,26 +1917,26 @@ void AXLayoutObject::lineBreaks(Vector<int>& lineBreaks) const
// Private.
//
-bool AXLayoutObject::isAllowedChildOfTree() const
+AXObject* AXLayoutObject::treeAncestorDisallowingChild() const
{
// Determine if this is in a tree. If so, we apply special behavior to make it work like an AXOutline.
AXObject* axObj = parentObject();
- bool isInTree = false;
+ AXObject* treeAncestor = 0;
while (axObj) {
if (axObj->isTree()) {
- isInTree = true;
+ treeAncestor = axObj;
break;
}
axObj = axObj->parentObject();
}
// If the object is in a tree, only tree items should be exposed (and the children of tree items).
- if (isInTree) {
+ if (treeAncestor) {
AccessibilityRole role = roleValue();
if (role != TreeItemRole && role != StaticTextRole)
- return false;
+ return treeAncestor;
}
- return true;
+ return 0;
}
void AXLayoutObject::ariaListboxSelectedChildren(AccessibilityChildrenVector& result)

Powered by Google App Engine
This is Rietveld 408576698