Skip to content

Commit

Permalink
AX: optimize accessibilityIsIgnored computation
Browse files Browse the repository at this point in the history
https://bugs.webkit.org/show_bug.cgi?id=262733
rdar://problem/116541936

Reviewed by Tyler Wilcock.

We currently traverse the ancestor hierarchy three times to compute each of
isAXHidden, isPresentationalChildOfAriaRole, and isDescendantOfBarrenParent
—all of which we gather to compute accessibility is ignored.

This patch combines those ancestor traversals into one, using
generateIsIgnoredFromParentData. This saves up to roughly 20% of samples for
accessibilityIsIgnored across various sites.

* Source/WebCore/accessibility/AccessibilityNodeObject.cpp:
(WebCore::AccessibilityNodeObject::canHaveChildren const):
(WebCore::AccessibilityNodeObject::computeAccessibilityIsIgnored const):
* Source/WebCore/accessibility/AccessibilityObject.cpp:
(WebCore::AccessibilityObject::isARIAHidden const):
(WebCore::AccessibilityObject::isAXHidden const):
(WebCore::AccessibilityObject::defaultObjectInclusion const):
(WebCore::AccessibilityObject::setIsIgnoredFromParentDataForChild):
(WebCore::AccessibilityObject::isPresentationalChildOfAriaRole const): Deleted.
* Source/WebCore/accessibility/AccessibilityObject.h:
(WebCore::AccessibilityObject::hasImplicitGenericRole const):
* Source/WebCore/accessibility/AccessibilityRenderObject.cpp:
(WebCore::AccessibilityRenderObject::computeAccessibilityIsIgnored const):

Canonical link: https://commits.webkit.org/269313@main
  • Loading branch information
hoffmanjoshua authored and twilco committed Oct 13, 2023
1 parent c2a6009 commit c24f056
Show file tree
Hide file tree
Showing 4 changed files with 32 additions and 21 deletions.
6 changes: 3 additions & 3 deletions Source/WebCore/accessibility/AccessibilityNodeObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -591,7 +591,10 @@ bool AccessibilityNodeObject::canHaveChildren() const
// Elements that should not have children.
switch (roleValue()) {
case AccessibilityRole::Button:
#if !USE(ATSPI)
// GTK/ATSPI layout tests expect popup buttons to have children.
case AccessibilityRole::PopUpButton:
#endif
case AccessibilityRole::Checkbox:
case AccessibilityRole::RadioButton:
case AccessibilityRole::Tab:
Expand Down Expand Up @@ -655,9 +658,6 @@ bool AccessibilityNodeObject::computeAccessibilityIsIgnored() const
return false;
if (decision == AccessibilityObjectInclusion::IgnoreObject)
return true;
// If this element is within a parent that cannot have children, it should not be exposed.
if (isDescendantOfBarrenParent())
return true;

auto role = roleValue();
return role == AccessibilityRole::Ignored || role == AccessibilityRole::Unknown;
Expand Down
41 changes: 28 additions & 13 deletions Source/WebCore/accessibility/AccessibilityObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3915,6 +3915,11 @@ bool AccessibilityObject::accessibilityIsIgnoredByDefault() const
return defaultObjectInclusion() == AccessibilityObjectInclusion::IgnoreObject;
}

bool AccessibilityObject::isARIAHidden() const
{
return equalLettersIgnoringASCIICase(getAttribute(aria_hiddenAttr), "true"_s) && !isFocused();
}

// ARIA component of hidden definition.
// https://www.w3.org/TR/wai-aria/#dfn-hidden
bool AccessibilityObject::isAXHidden() const
Expand All @@ -3923,7 +3928,7 @@ bool AccessibilityObject::isAXHidden() const
return false;

return Accessibility::findAncestor<AccessibilityObject>(*this, true, [] (const AccessibilityObject& object) {
return equalLettersIgnoringASCIICase(object.getAttribute(aria_hiddenAttr), "true"_s) && !object.isFocused();
return object.isARIAHidden();
}) != nullptr;
}

Expand Down Expand Up @@ -3969,10 +3974,16 @@ AccessibilityObjectInclusion AccessibilityObject::defaultObjectInclusion() const
}

bool useParentData = !m_isIgnoredFromParentData.isNull();
if (useParentData ? m_isIgnoredFromParentData.isAXHidden : isAXHidden())
if (useParentData && (m_isIgnoredFromParentData.isAXHidden || m_isIgnoredFromParentData.isPresentationalChildOfAriaRole))
return AccessibilityObjectInclusion::IgnoreObject;

if (useParentData ? m_isIgnoredFromParentData.isPresentationalChildOfAriaRole : isPresentationalChildOfAriaRole())
if (isARIAHidden())
return AccessibilityObjectInclusion::IgnoreObject;

bool ignoreARIAHidden = isFocused();
if (Accessibility::findAncestor<AccessibilityObject>(*this, false, [&] (const auto& object) {
return (!ignoreARIAHidden && object.isARIAHidden()) || object.ariaRoleHasPresentationalChildren() || !object.canHaveChildren();
}))
return AccessibilityObjectInclusion::IgnoreObject;

// Include <dialog> elements and elements with role="dialog".
Expand Down Expand Up @@ -4332,13 +4343,6 @@ bool AccessibilityObject::ariaRoleHasPresentationalChildren() const
}
}

bool AccessibilityObject::isPresentationalChildOfAriaRole() const
{
return Accessibility::findAncestor(*this, false, [] (const auto& ancestor) {
return ancestor.ariaRoleHasPresentationalChildren();
});
}

void AccessibilityObject::setIsIgnoredFromParentDataForChild(AccessibilityObject* child)
{
if (!child)
Expand All @@ -4350,9 +4354,20 @@ void AccessibilityObject::setIsIgnoredFromParentDataForChild(AccessibilityObject
result.isPresentationalChildOfAriaRole = m_isIgnoredFromParentData.isPresentationalChildOfAriaRole || ariaRoleHasPresentationalChildren();
result.isDescendantOfBarrenParent = m_isIgnoredFromParentData.isDescendantOfBarrenParent || !canHaveChildren();
} else {
result.isAXHidden = child->isAXHidden();
result.isPresentationalChildOfAriaRole = child->isPresentationalChildOfAriaRole();
result.isDescendantOfBarrenParent = child->isDescendantOfBarrenParent();
if (child->isARIAHidden())
result.isAXHidden = true;

bool ignoreARIAHidden = child->isFocused();
for (auto* object = child->parentObject(); object; object = object->parentObject()) {
if (!result.isAXHidden && !ignoreARIAHidden && object->isARIAHidden())
result.isAXHidden = true;

if (!result.isPresentationalChildOfAriaRole && object->ariaRoleHasPresentationalChildren())
result.isPresentationalChildOfAriaRole = true;

if (!result.isDescendantOfBarrenParent && !object->canHaveChildren())
result.isDescendantOfBarrenParent = true;
}
}

child->setIsIgnoredFromParentData(result);
Expand Down
2 changes: 1 addition & 1 deletion Source/WebCore/accessibility/AccessibilityObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,7 @@ class AccessibilityObject : public AXCoreObject, public CanMakeWeakPtr<Accessibi
void recomputeIsIgnored();
AccessibilityObjectInclusion defaultObjectInclusion() const;
bool accessibilityIsIgnoredByDefault() const;
bool isARIAHidden() const;

bool isShowingValidationMessage() const;
String validationMessage() const;
Expand Down Expand Up @@ -361,7 +362,6 @@ class AccessibilityObject : public AXCoreObject, public CanMakeWeakPtr<Accessibi
virtual AccessibilityRole ariaRoleAttribute() const { return AccessibilityRole::Unknown; }
bool hasExplicitGenericRole() const { return ariaRoleAttribute() == AccessibilityRole::Generic; }
bool hasImplicitGenericRole() const { return roleValue() == AccessibilityRole::Generic && !hasExplicitGenericRole(); }
bool isPresentationalChildOfAriaRole() const;
bool ariaRoleHasPresentationalChildren() const;
bool inheritsPresentationalRole() const override { return false; }

Expand Down
4 changes: 0 additions & 4 deletions Source/WebCore/accessibility/AccessibilityRenderObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1066,10 +1066,6 @@ bool AccessibilityRenderObject::computeAccessibilityIsIgnored() const
if (decision == AccessibilityObjectInclusion::IgnoreObject)
return true;

// If this element is within a parent that cannot have children, it should not be exposed.
if (isDescendantOfBarrenParent())
return true;

if (roleValue() == AccessibilityRole::Ignored)
return true;

Expand Down

0 comments on commit c24f056

Please sign in to comment.