Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
AX: display:contents elements never return any selected AX children d…
…espite having them

https://bugs.webkit.org/show_bug.cgi?id=255845
rdar://problem/108428630

Reviewed by Chris Fleizach.

Move the non-renderer-specific `selectedChildren` functions from
AccessibilityRenderObject to AccessibilityObject.

Testcase added to aria-selected.html.

* LayoutTests/accessibility/aria-selected.html:
* Source/WebCore/accessibility/AccessibilityListBox.cpp:
(WebCore::AccessibilityListBox::selectedChildren):
* Source/WebCore/accessibility/AccessibilityListBox.h:
* Source/WebCore/accessibility/AccessibilityMenuListPopup.cpp:
(WebCore::AccessibilityMenuListPopup::selectedChildren):
* Source/WebCore/accessibility/AccessibilityMenuListPopup.h:
* Source/WebCore/accessibility/AccessibilityNodeObject.cpp:
(WebCore::accessibleNameForNode):
* Source/WebCore/accessibility/AccessibilityObject.cpp:
(WebCore::AccessibilityObject::ariaSelectedRows):
(WebCore::AccessibilityObject::ariaListboxSelectedChildren):
(WebCore::AccessibilityObject::selectedChildren):
* Source/WebCore/accessibility/AccessibilityObject.h:
* Source/WebCore/accessibility/AccessibilityObjectInterface.h:
* Source/WebCore/accessibility/AccessibilityRenderObject.cpp:
(WebCore::AccessibilityRenderObject::ariaSelectedRows): Deleted.
(WebCore::AccessibilityRenderObject::ariaListboxSelectedChildren): Deleted.
(WebCore::AccessibilityRenderObject::selectedChildren): Deleted.
* Source/WebCore/accessibility/AccessibilityRenderObject.h:
* Source/WebCore/accessibility/isolatedtree/AXIsolatedObject.cpp:
(WebCore::AXIsolatedObject::initializeProperties):
* Source/WebCore/accessibility/isolatedtree/AXIsolatedObject.h:
* Source/WebCore/accessibility/mac/WebAccessibilityObjectWrapperMac.mm:
(-[WebAccessibilityObjectWrapper accessibilityAttributeValue:]):
* Source/WebCore/inspector/InspectorAuditAccessibilityObject.cpp:
(WebCore::InspectorAuditAccessibilityObject::getSelectedChildNodes):
* Source/WebCore/inspector/agents/InspectorDOMAgent.cpp:
(WebCore::InspectorDOMAgent::buildObjectForAccessibilityProperties):
* Source/WebCore/accessibility/atspi/AccessibilityObjectSelectionAtspi.cpp:
(WebCore::AccessibilityObjectAtspi::selectionCount const):
(WebCore::AccessibilityObjectAtspi::selectedChild const):
(WebCore::AccessibilityObjectAtspi::deselectSelectedChild const):
(WebCore::AccessibilityObjectAtspi::selectAll const):
(WebCore::AccessibilityObjectAtspi::clearSelection const):

Canonical link: https://commits.webkit.org/263339@main
  • Loading branch information
twilco committed Apr 24, 2023
1 parent f9f7390 commit 71c4047
Show file tree
Hide file tree
Showing 17 changed files with 133 additions and 156 deletions.
2 changes: 1 addition & 1 deletion LayoutTests/accessibility/aria-selected.html
Expand Up @@ -11,7 +11,7 @@
<div role="tab" aria-label="second" aria-selected="true">2</div>
<div role="tab" aria-label="third">3</div>
</div>
<div role="tablist" id="tablist-with-display-contents" tabindex="0">
<div role="tablist" id="tablist-with-display-contents" tabindex="0" style="display:contents">
<div role="tab" aria-label="first">1</div>
<div role="tab" aria-label="second" aria-selected="true" style="display:contents">2</div>
<div role="tab" aria-label="third">3</div>
Expand Down
8 changes: 4 additions & 4 deletions Source/WebCore/accessibility/AccessibilityListBox.cpp
Expand Up @@ -101,17 +101,17 @@ void AccessibilityListBox::setSelectedChildren(const AccessibilityChildrenVector
}
}

void AccessibilityListBox::selectedChildren(AccessibilityChildrenVector& result)
AXCoreObject::AccessibilityChildrenVector AccessibilityListBox::selectedChildren()
{
ASSERT(result.isEmpty());

if (!childrenInitialized())
addChildren();

AccessibilityChildrenVector result;
for (const auto& child : m_children) {
if (downcast<AccessibilityListBoxOption>(*child).isSelected())
result.append(child.get());
}
}
return result;
}

AXCoreObject::AccessibilityChildrenVector AccessibilityListBox::visibleChildren()
Expand Down
2 changes: 1 addition & 1 deletion Source/WebCore/accessibility/AccessibilityListBox.h
Expand Up @@ -41,7 +41,7 @@ class AccessibilityListBox final : public AccessibilityRenderObject {
WEBCORE_EXPORT void setSelectedChildren(const AccessibilityChildrenVector&) override;
AccessibilityRole roleValue() const override { return AccessibilityRole::ListBox; }

void selectedChildren(AccessibilityChildrenVector&) override;
AccessibilityChildrenVector selectedChildren() final;
AccessibilityChildrenVector visibleChildren() final;

void addChildren() override;
Expand Down
7 changes: 4 additions & 3 deletions Source/WebCore/accessibility/AccessibilityMenuListPopup.cpp
Expand Up @@ -68,20 +68,21 @@ bool AccessibilityMenuListPopup::computeAccessibilityIsIgnored() const
return accessibilityIsIgnoredByDefault();
}

void AccessibilityMenuListPopup::selectedChildren(AccessibilityChildrenVector& result)
AXCoreObject::AccessibilityChildrenVector AccessibilityMenuListPopup::selectedChildren()
{
ASSERT(result.isEmpty());
if (!canHaveSelectedChildren())
return;
return { };

if (!childrenInitialized())
addChildren();

AccessibilityChildrenVector result;
for (const auto& child : m_children) {
auto* liveChild = dynamicDowncast<AccessibilityObject>(child.get());
if (liveChild && liveChild->isMenuListOption() && liveChild->isSelected())
result.append(child.get());
}
return result;
}

AccessibilityMenuListOption* AccessibilityMenuListPopup::menuListOptionAccessibilityObject(HTMLElement* element) const
Expand Down
2 changes: 1 addition & 1 deletion Source/WebCore/accessibility/AccessibilityMenuListPopup.h
Expand Up @@ -56,7 +56,7 @@ class AccessibilityMenuListPopup final : public AccessibilityMockObject {
void addChildren() override;
void handleChildrenChanged();
bool computeAccessibilityIsIgnored() const override;
void selectedChildren(AccessibilityChildrenVector&) override;
AccessibilityChildrenVector selectedChildren() final;

AccessibilityMenuListOption* menuListOptionAccessibilityObject(HTMLElement*) const;
};
Expand Down
8 changes: 4 additions & 4 deletions Source/WebCore/accessibility/AccessibilityNodeObject.cpp
Expand Up @@ -2321,21 +2321,21 @@ static String accessibleNameForNode(Node* node, Node* labelledbyNode)

// The Accname specification states that if the name is being calculated for a combobox
// or listbox inside a labeling element, return the text alternative of the chosen option.
AccessibilityObject::AccessibilityChildrenVector children;
AccessibilityObject::AccessibilityChildrenVector selectedChildren;
if (axObject->isListBox())
axObject->selectedChildren(children);
selectedChildren = axObject->selectedChildren();
else if (axObject->isComboBox()) {
for (const auto& child : axObject->children()) {
if (child->isListBox()) {
child->selectedChildren(children);
selectedChildren = child->selectedChildren();
break;
}
}
}

StringBuilder builder;
String childText;
for (const auto& child : children)
for (const auto& child : selectedChildren)
appendNameToStringBuilder(builder, accessibleNameForNode(child->node()));

childText = builder.toString();
Expand Down
98 changes: 97 additions & 1 deletion Source/WebCore/accessibility/AccessibilityObject.cpp
Expand Up @@ -4029,7 +4029,103 @@ bool AccessibilityObject::isContainedBySecureField() const
Element* element = node->shadowHost();
return is<HTMLInputElement>(element) && downcast<HTMLInputElement>(*element).isSecureField();
}


AXCoreObject::AccessibilityChildrenVector AccessibilityObject::ariaSelectedRows()
{
bool isMulti = isMultiSelectable();

AccessibilityChildrenVector result;
// Prefer active descendant over aria-selected.
auto* activeDescendant = this->activeDescendant();
if (activeDescendant && (activeDescendant->isTreeItem() || activeDescendant->isTableRow())) {
result.append(activeDescendant);
if (!isMulti)
return result;
}

auto rowsIteration = [&](const auto& rows) {
for (auto& rowCoreObject : rows) {
auto* row = dynamicDowncast<AccessibilityObject>(rowCoreObject.get());
if (!row)
continue;

if (row->isSelected() || row->isActiveDescendantOfFocusedContainer()) {
result.append(row);
if (!isMulti)
break;
}
}
};

if (isTree()) {
AccessibilityChildrenVector allRows;
ariaTreeRows(allRows);
rowsIteration(allRows);
} else if (auto* axTable = dynamicDowncast<AccessibilityTable>(this)) {
if (axTable->supportsSelectedRows() && axTable->isExposable())
rowsIteration(axTable->rows());
}
return result;
}

AXCoreObject::AccessibilityChildrenVector AccessibilityObject::ariaListboxSelectedChildren()
{
bool isMulti = isMultiSelectable();

AccessibilityChildrenVector result;
for (const auto& child : children()) {
auto* axChild = dynamicDowncast<AccessibilityObject>(child.get());
// Every child should have aria-role option, and if so, check for selected attribute/state.
if (!axChild || axChild->ariaRoleAttribute() != AccessibilityRole::ListBoxOption)
continue;

if (axChild->isSelected() || axChild->isActiveDescendantOfFocusedContainer()) {
result.append(axChild);
if (!isMulti)
return result;
}
}
return result;
}

AXCoreObject::AccessibilityChildrenVector AccessibilityObject::selectedChildren()
{
if (!canHaveSelectedChildren())
return { };

AccessibilityChildrenVector result;
switch (roleValue()) {
case AccessibilityRole::ListBox:
// native list boxes would be AccessibilityListBoxes, so only check for aria list boxes
result = ariaListboxSelectedChildren();
break;
case AccessibilityRole::Grid:
case AccessibilityRole::Tree:
case AccessibilityRole::TreeGrid:
result = ariaSelectedRows();
break;
case AccessibilityRole::TabList:
if (auto* selectedTab = selectedTabItem())
result = { selectedTab };
break;
case AccessibilityRole::List:
if (auto* selectedListItemChild = selectedListItem())
result = { selectedListItemChild };
break;
case AccessibilityRole::Menu:
case AccessibilityRole::MenuBar:
if (auto* descendant = activeDescendant())
result = { descendant };
else if (auto* focusedElement = dynamicDowncast<AccessibilityObject>(focusedUIElement()))
result = { focusedElement };
break;
default:
ASSERT_NOT_REACHED();
break;
}
return result;
}

AccessibilityObject* AccessibilityObject::selectedListItem()
{
for (const auto& child : children()) {
Expand Down
4 changes: 3 additions & 1 deletion Source/WebCore/accessibility/AccessibilityObject.h
Expand Up @@ -509,7 +509,7 @@ class AccessibilityObject : public AXCoreObject, public CanMakeWeakPtr<Accessibi
#endif
bool isDetachedFromParent() override { return false; }

void selectedChildren(AccessibilityChildrenVector&) override { }
AccessibilityChildrenVector selectedChildren() override;
void setSelectedChildren(const AccessibilityChildrenVector&) override { }
AccessibilityChildrenVector visibleChildren() override { return { }; }
bool shouldFocusActiveDescendant() const;
Expand Down Expand Up @@ -799,6 +799,8 @@ class AccessibilityObject : public AXCoreObject, public CanMakeWeakPtr<Accessibi
bool accessibilityIsIgnoredWithoutCache(AXObjectCache*) const;
void setLastKnownIsIgnoredValue(bool);
void ariaTreeRows(AccessibilityChildrenVector& rows, AccessibilityChildrenVector& ancestors);
AccessibilityChildrenVector ariaListboxSelectedChildren();
AccessibilityChildrenVector ariaSelectedRows();

// Special handling of click point for links.
IntPoint linkClickPoint();
Expand Down
Expand Up @@ -1251,7 +1251,7 @@ class AXCoreObject : public ThreadSafeRefCounted<AXCoreObject> {

bool canHaveSelectedChildren() const;
bool canHaveSelectedCells() const;
virtual void selectedChildren(AccessibilityChildrenVector&) = 0;
virtual AccessibilityChildrenVector selectedChildren() = 0;
virtual void setSelectedChildren(const AccessibilityChildrenVector&) = 0;
virtual AccessibilityChildrenVector visibleChildren() = 0;
AccessibilityChildrenVector tabChildren();
Expand Down
97 changes: 0 additions & 97 deletions Source/WebCore/accessibility/AccessibilityRenderObject.cpp
Expand Up @@ -2749,103 +2749,6 @@ bool AccessibilityRenderObject::canHaveChildren() const
return m_renderer && AccessibilityNodeObject::canHaveChildren();
}

void AccessibilityRenderObject::ariaSelectedRows(AccessibilityChildrenVector& result)
{
// Determine which rows are selected.
bool isMulti = isMultiSelectable();

// Prefer active descendant over aria-selected.
AccessibilityObject* activeDesc = activeDescendant();
if (activeDesc && (activeDesc->isTreeItem() || activeDesc->isTableRow())) {
result.append(activeDesc);
if (!isMulti)
return;
}

// Get all the rows.
auto rowsIteration = [&](const auto& rows) {
for (auto& rowCoreObject : rows) {
auto* row = dynamicDowncast<AccessibilityObject>(rowCoreObject.get());
if (!row)
continue;

if (row->isSelected() || row->isActiveDescendantOfFocusedContainer()) {
result.append(row);
if (!isMulti)
break;
}
}
};
if (isTree()) {
AccessibilityChildrenVector allRows;
ariaTreeRows(allRows);
rowsIteration(allRows);
} else if (is<AccessibilityTable>(*this)) {
auto& thisTable = downcast<AccessibilityTable>(*this);
if (thisTable.isExposable() && thisTable.supportsSelectedRows())
rowsIteration(thisTable.rows());
}
}

void AccessibilityRenderObject::ariaListboxSelectedChildren(AccessibilityChildrenVector& result)
{
bool isMulti = isMultiSelectable();

for (const auto& child : children()) {
auto* axChild = dynamicDowncast<AccessibilityObject>(child.get());
// Every child should have aria-role option, and if so, check for selected attribute/state.
if (!axChild || axChild->ariaRoleAttribute() != AccessibilityRole::ListBoxOption)
continue;

if (axChild->isSelected() || axChild->isActiveDescendantOfFocusedContainer()) {
result.append(axChild);
if (!isMulti)
return;
}
}
}

void AccessibilityRenderObject::selectedChildren(AccessibilityChildrenVector& result)
{
ASSERT(result.isEmpty());

if (!canHaveSelectedChildren())
return;

switch (roleValue()) {
case AccessibilityRole::ListBox:
// native list boxes would be AccessibilityListBoxes, so only check for aria list boxes
ariaListboxSelectedChildren(result);
return;
case AccessibilityRole::Grid:
case AccessibilityRole::Tree:
case AccessibilityRole::TreeGrid:
ariaSelectedRows(result);
return;
case AccessibilityRole::TabList:
if (AXCoreObject* selectedTab = selectedTabItem())
result.append(selectedTab);
return;
case AccessibilityRole::List:
if (auto* selectedListItemChild = selectedListItem())
result.append(selectedListItemChild);
return;
case AccessibilityRole::Menu:
case AccessibilityRole::MenuBar:
if (AccessibilityObject* descendant = activeDescendant()) {
result.append(descendant);
return;
}
if (AccessibilityObject* focusedElement = static_cast<AccessibilityObject*>(focusedUIElement())) {
result.append(focusedElement);
return;
}
return;
default:
ASSERT_NOT_REACHED();
}
}

void AccessibilityRenderObject::setAccessibleName(const AtomString& name)
{
// Setting the accessible name can store the value in the DOM
Expand Down
4 changes: 0 additions & 4 deletions Source/WebCore/accessibility/AccessibilityRenderObject.h
Expand Up @@ -120,7 +120,6 @@ class AccessibilityRenderObject : public AccessibilityNodeObject {

void addChildren() override;
bool canHaveChildren() const override;
void selectedChildren(AccessibilityChildrenVector&) override;

VisiblePositionRange visiblePositionRange() const override;
VisiblePositionRange visiblePositionRangeForLine(unsigned) const override;
Expand Down Expand Up @@ -166,7 +165,6 @@ class AccessibilityRenderObject : public AccessibilityNodeObject {

private:
bool isAccessibilityRenderObject() const final { return true; }
void ariaListboxSelectedChildren(AccessibilityChildrenVector&);
bool isAllowedChildOfTree() const;
PlainTextRange documentBasedSelectedTextRange() const;
Element* rootEditableElementForPosition(const Position&) const;
Expand Down Expand Up @@ -211,8 +209,6 @@ class AccessibilityRenderObject : public AccessibilityNodeObject {
bool supportsExpandedTextValue() const override;
void updateRoleAfterChildrenCreation();

void ariaSelectedRows(AccessibilityChildrenVector&);

bool inheritsPresentationalRole() const override;

bool shouldGetTextFromNode(AccessibilityTextUnderElementMode) const;
Expand Down

0 comments on commit 71c4047

Please sign in to comment.