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

Unified Diff: Source/modules/accessibility/InspectorAccessibilityAgent.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: 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/InspectorAccessibilityAgent.cpp
diff --git a/Source/modules/accessibility/InspectorAccessibilityAgent.cpp b/Source/modules/accessibility/InspectorAccessibilityAgent.cpp
index 82584d2434012dea0d2cf75da9684ef5eba5d1b7..a8133f675e58b81cf80a943b5cdd51c7f3599859 100644
--- a/Source/modules/accessibility/InspectorAccessibilityAgent.cpp
+++ b/Source/modules/accessibility/InspectorAccessibilityAgent.cpp
@@ -6,12 +6,19 @@
#include "modules/accessibility/InspectorAccessibilityAgent.h"
+#include "core/HTMLNames.h"
#include "core/dom/AXObjectCache.h"
#include "core/dom/DOMNodeIds.h"
#include "core/dom/Element.h"
+#include "core/dom/ElementTraversal.h"
+#include "core/html/HTMLDialogElement.h"
+#include "core/html/HTMLFrameOwnerElement.h"
+#include "core/html/HTMLLabelElement.h"
#include "core/inspector/InspectorDOMAgent.h"
#include "core/inspector/InspectorState.h"
#include "core/inspector/InspectorStyleSheet.h"
+#include "core/layout/LayoutBlockFlow.h"
+#include "core/layout/LayoutBoxModelObject.h"
#include "core/page/Page.h"
#include "modules/accessibility/AXObject.h"
#include "modules/accessibility/AXObjectCacheImpl.h"
@@ -19,7 +26,13 @@
namespace blink {
+using HTMLNames::altAttr;
+using HTMLNames::aria_describedbyAttr;
+using HTMLNames::aria_helpAttr;
+using HTMLNames::titleAttr;
using TypeBuilder::Accessibility::AXGlobalStates;
+using TypeBuilder::Accessibility::AXIgnoredNode;
+using TypeBuilder::Accessibility::AXIgnoredReasons;
using TypeBuilder::Accessibility::AXLiveRegionAttributes;
using TypeBuilder::Accessibility::AXNode;
using TypeBuilder::Accessibility::AXNodeId;
@@ -49,7 +62,6 @@ PassRefPtr<AXProperty> createProperty(AXLiveRegionAttributes::Enum name, PassRef
return createProperty(TypeBuilder::getEnumConstantValue(name), value);
}
-
PassRefPtr<AXProperty> createProperty(AXRelationshipAttributes::Enum name, PassRefPtr<AXValue> value)
{
return createProperty(TypeBuilder::getEnumConstantValue(name), value);
@@ -65,6 +77,10 @@ PassRefPtr<AXProperty> createProperty(AXWidgetStates::Enum name, PassRefPtr<AXVa
return createProperty(TypeBuilder::getEnumConstantValue(name), value);
}
+PassRefPtr<AXProperty> createProperty(AXIgnoredReasons::Enum name, PassRefPtr<AXValue> value)
+{
+ return createProperty(TypeBuilder::getEnumConstantValue(name), value);
+}
PassRefPtr<AXValue> createValue(String value, AXValueType::Enum type = AXValueType::String)
{
@@ -381,9 +397,8 @@ void fillRelationships(AXObject* axObject, PassRefPtr<TypeBuilder::Array<AXPrope
results.clear();
}
-PassRefPtr<AXNode> buildObjectForNode(Node* node, AXObject* axObject, AXObjectCacheImpl* cacheImpl, PassRefPtr<TypeBuilder::Array<AXProperty>> properties)
+PassRefPtr<AXValue> createRoleNameValue(AccessibilityRole role)
{
- AccessibilityRole role = axObject->roleValue();
AtomicString roleName = AXObject::roleName(role);
RefPtr<AXValue> roleNameValue;
if (!roleName.isNull()) {
@@ -391,7 +406,190 @@ PassRefPtr<AXNode> buildObjectForNode(Node* node, AXObject* axObject, AXObjectCa
} else {
roleNameValue = createValue(AXObject::internalRoleName(role), AXValueType::InternalRole);
}
- RefPtr<AXNode> nodeObject = AXNode::create().setNodeId(String::number(axObject->axObjectID())).setRole(roleNameValue).setProperties(properties);
+ return roleNameValue;
+}
+
+PassRefPtr<TypeBuilder::Array<AXProperty>> buildIgnoredReasons(Node* node, AXObject* axObject, AXObjectCacheImpl* cacheImpl)
dmazzoni 2015/04/09 05:46:10 Should this be in AXLayoutObject? It feels like we
aboxhall 2015/04/09 20:15:36 Yeah, I considered that. It would certainly be cle
+{
+ RefPtr<TypeBuilder::Array<AXProperty>> ignoredReasons = TypeBuilder::Array<AXProperty>::create();
+ // hidden
+ const AXObject* hiddenRoot = axObject->ariaHiddenRoot();
+ if (hiddenRoot) {
+ if (hiddenRoot == axObject)
+ ignoredReasons->addItem(createProperty(AXIgnoredReasons::AriaHidden, createBooleanValue(true)));
+ else
+ ignoredReasons->addItem(createProperty(AXIgnoredReasons::AriaHiddenRoot, createRelatedNodeValue(hiddenRoot)));
+ return ignoredReasons;
+ }
+
+ // inert
+ if (node->isInert()) {
+ HTMLDialogElement* activeModalDialog = node->document().activeModalDialog();
+ while (!activeModalDialog) {
+ HTMLFrameOwnerElement* ownerElement = node->document().ownerElement();
+ if (!ownerElement)
+ break;
+ if (ownerElement && ownerElement->isInert())
+ activeModalDialog = ownerElement->document().activeModalDialog();
+ }
+
+ if (activeModalDialog) {
+ AXObject* dialogObject = cacheImpl->getOrCreate(activeModalDialog);
+ if (dialogObject) {
+ ignoredReasons->addItem(createProperty(AXIgnoredReasons::ActiveModalDialog, createRelatedNodeValue(dialogObject)));
+ return ignoredReasons;
+ }
+ }
+ }
+
+ // ancestor can't have children (incl menuitem/menubutton parent)
+ if (axObject->isDescendantOfBarrenParent()) {
+ AXObject* parent = axObject->parentObject();
+ while (parent && parent->canHaveChildren())
+ parent = parent->parentObject();
+ if (parent) {
+ ignoredReasons->addItem(createProperty(AXIgnoredReasons::AncestorDisallowsChild, createRelatedNodeValue(parent)));
+ return ignoredReasons;
+ }
+ }
+
+ // role == ignored || role == none || role == presentational
+ AccessibilityRole role = axObject->roleValue();
+ if (role == IgnoredRole || role == NoneRole || role == PresentationalRole) {
+ ignoredReasons->addItem(createProperty(AXIgnoredReasons::IgnoredRole, createRoleNameValue(role)));
+ return ignoredReasons;
+ }
+
+ // inherits presentational role
+ if (axObject->hasInheritedPresentationalRole()) {
+ AXObject* parent = axObject->parentObject();
+ while (parent && !parent->isPresentational())
+ parent = parent->parentObject();
+ if (parent) {
+ ignoredReasons->addItem(createProperty(AXIgnoredReasons::InheritsPresentation, createRelatedNodeValue(parent)));
+ ignoredReasons->addItem(createProperty(AXIgnoredReasons::Focusable, createBooleanValue(node->isElementNode() && toElement(node)->supportsFocus())));
+ return ignoredReasons;
+ }
+ }
+
+ // disallowed child of ARIA tree
+ AXObject* potentialTree = axObject->parentObject();
+ while (potentialTree && !potentialTree->isTree())
+ potentialTree = potentialTree->parentObject();
+ if (potentialTree && role != TreeItemRole && role != StaticTextRole) {
+ ignoredReasons->addItem(createProperty(AXIgnoredReasons::AncestorDisallowsChild, createRelatedNodeValue(potentialTree)));
+ return ignoredReasons;
+ }
+
+ if (LayoutObject* layoutObject = axObject->layoutObject()) {
+ // Popup menu
+ for (LayoutObject* parent = layoutObject->parent(); parent; parent = parent->parent()) {
+ if (parent->isBoxModelObject() && toLayoutBoxModelObject(parent)->isMenuList()) {
+ AXObject* parentAXObject = cacheImpl->getOrCreate(parent);
+ if (parentAXObject) {
+ ignoredReasons->addItem(createProperty(AXIgnoredReasons::AncestorDisallowsChild, createRelatedNodeValue(parentAXObject)));
+ return ignoredReasons;
+ }
+ }
+ }
+
+ // static text inside MenuItem/MenuButton
+ if (layoutObject->isText()) {
+ AXObject* parent = axObject->parentObjectUnignored();
+ if (parent && (parent->ariaRoleAttribute() == MenuItemRadioRole || parent->ariaRoleAttribute() == MenuButtonRole)) {
+ ignoredReasons->addItem(createProperty(AXIgnoredReasons::AncestorDisallowsChild, createRelatedNodeValue(parent)));
+ return ignoredReasons;
+ }
+ }
+
+ // Empty/whitespace only layoutText
+ LayoutText* layoutText = toLayoutText(layoutObject);
+ if (!layoutText->firstTextBox() || layoutText->text().impl()->containsOnlyWhitespace()) {
+ ignoredReasons->addItem(createProperty(AXIgnoredReasons::EmptyText, createBooleanValue(true)));
+ return ignoredReasons;
+ }
+
+ // Unfocusable layoutBlock
+ if (layoutObject->isLayoutBlockFlow() && layoutObject->childrenInline() && !axObject->canSetFocusAttribute() && (!toLayoutBlockFlow(layoutObject)->firstLineBox())) {
+ ignoredReasons->addItem(createProperty(AXIgnoredReasons::Focusable, createBooleanValue(false)));
dmazzoni 2015/04/09 05:46:10 "not focusable" seems to be a strange reason to me
aboxhall 2015/04/09 20:15:36 As you say, it's more of a heuristic than a reason
+ return ignoredReasons;
+ }
+ }
+
+ // is [part of] label for checkbox or radio
+ if (!axObject->isControl() && !node->isLink()) {
+ HTMLLabelElement* label = Traversal<HTMLLabelElement>::firstAncestorOrSelf(*node);
+ if (label) {
+ if (!label->isSameNode(node)) {
+ AXObject* labelAXObject = cacheImpl->getOrCreate(label);
+ ignoredReasons->addItem(createProperty(AXIgnoredReasons::LabelContainer, createRelatedNodeValue(labelAXObject)));
+ return ignoredReasons;
+ }
+
+ HTMLElement* correspondingControl = label->control();
+ if (correspondingControl) {
+ AXObject* controlAXObject = cacheImpl->getOrCreate(correspondingControl);
+ if (controlAXObject) {
+ ignoredReasons->addItem(createProperty(AXIgnoredReasons::LabelFor, createRelatedNodeValue(controlAXObject)));
+ return ignoredReasons;
+ }
+ }
+ }
+ }
+
+ // is <span>
+ if (isHTMLSpanElement(node)) {
+ Element* element = toElement(node);
+ ignoredReasons->addItem(createProperty(AXIgnoredReasons::IgnoredTagName, createValue(element->tagName())));
+ return ignoredReasons;
+ }
+
+ if (axObject->isImage()) {
+ if (!axObject->canSetFocusAttribute()) {
+ // is image with zero alt text
+ Element* element = toElement(node);
+ const AtomicString& alt = element->getAttribute(altAttr);
+ if (alt.string().containsOnlyWhitespace() && !alt.isNull()) {
+ ignoredReasons->addItem(createProperty(AXIgnoredReasons::Focusable, createBooleanValue(false)));
+
+ ignoredReasons->addItem(createProperty(AXIgnoredReasons::Alt, createValue(alt.string())));
+ return ignoredReasons;
+ }
+
+ // TODO(aboxhall): is spacer image
+ }
+ }
+
+ // is canvas with no fallback
+ if (axObject->isCanvas()) {
+ if (!ElementTraversal::firstChild(*node)) {
+ ignoredReasons->addItem(createProperty(AXIgnoredReasons::NoFallbackContent, createBooleanValue(true)));
+ return ignoredReasons;
+ }
+ }
+
+ // is other and has no text alternative
+ if (axObject->getAttribute(aria_helpAttr).isEmpty() && axObject->getAttribute(aria_describedbyAttr).isEmpty() && axObject->getAttribute(altAttr).isEmpty() && axObject->getAttribute(titleAttr).isEmpty() && axObject->accessibilityDescription().isEmpty()) {
+ ignoredReasons->addItem(createProperty(AXIgnoredReasons::NoTextAlternative, createBooleanValue(true)));
+ return ignoredReasons;
+ }
+ return ignoredReasons;
+}
+
+PassRefPtr<AXNode> buildObjectForIgnoredNode(Node* node, AXObject* axObject, AXObjectCacheImpl* cacheImpl)
+{
+ RefPtr<TypeBuilder::Array<AXProperty>> ignoredReasons = buildIgnoredReasons(node, axObject, cacheImpl);
+ RefPtr<AXNode> ignoredNodeObject = AXNode::create().setNodeId(String::number(axObject->axObjectID())).setIgnored(true);
+ ignoredNodeObject->setIgnoredReasons(ignoredReasons);
+ return ignoredNodeObject;
+}
+
+PassRefPtr<AXNode> buildObjectForNode(Node* node, AXObject* axObject, AXObjectCacheImpl* cacheImpl, PassRefPtr<TypeBuilder::Array<AXProperty>> properties)
+{
+ AccessibilityRole role = axObject->roleValue();
+ RefPtr<AXNode> nodeObject = AXNode::create().setNodeId(String::number(axObject->axObjectID())).setIgnored(false);
+ nodeObject->setRole(createRoleNameValue(role));
+ nodeObject->setProperties(properties);
String computedName = cacheImpl->computedNameForNode(node);
if (!computedName.isEmpty())
nodeObject->setName(createValue(computedName));
@@ -431,6 +629,11 @@ void InspectorAccessibilityAgent::getAXNode(ErrorString* errorString, int nodeId
if (!axObject)
return;
+ if (axObject->accessibilityIsIgnored()) {
+ accessibilityNode = buildObjectForIgnoredNode(node, axObject, cacheImpl);
+ return;
+ }
+
RefPtr<TypeBuilder::Array<AXProperty>> properties = TypeBuilder::Array<AXProperty>::create();
fillLiveRegionProperties(axObject, properties);
fillGlobalStates(axObject, properties);
« Source/devtools/protocol.json ('K') | « Source/modules/accessibility/AXLayoutObject.cpp ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698