Skip to content
Permalink
Browse files
AX: Update the isolated tree in response to dynamic aria-orientation …
…changes

https://bugs.webkit.org/show_bug.cgi?id=241543

Reviewed by Chris Fleizach.

This patch also makes aria-orientation work for node-only objects, like
those with display:contents.

* LayoutTests/accessibility/aria-orientation-expected.txt:
* LayoutTests/accessibility/aria-orientation.html:
* Source/WebCore/accessibility/AXLogger.cpp:
(WebCore::operator<<):
* Source/WebCore/accessibility/atspi/AXObjectCacheAtspi.cpp
* Source/WebCore/accessibility/AXObjectCache.cpp:
(WebCore::AXObjectCache::handleAttributeChange):
(WebCore::AXObjectCache::updateIsolatedTree):
* Source/WebCore/accessibility/AXObjectCache.h:
* Source/WebCore/accessibility/AccessibilityNodeObject.cpp:
(WebCore::AccessibilityNodeObject::orientation const):
* Source/WebCore/accessibility/AccessibilityNodeObject.h:
* Source/WebCore/accessibility/AccessibilityRenderObject.cpp:
(WebCore::AccessibilityRenderObject::orientation const): Deleted.
* Source/WebCore/accessibility/AccessibilityRenderObject.h:
* Source/WebCore/accessibility/isolatedtree/AXIsolatedTree.cpp:
(WebCore::AXIsolatedTree::updateNodeProperty):

Canonical link: https://commits.webkit.org/251497@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@295492 268f45cc-cd09-0410-ab3c-d52691b4dbfc
  • Loading branch information
twilco committed Jun 13, 2022
1 parent 5f855f2 commit 199b01f64d5ac5ebecd1a2f7ba0a257e0a2b40ab
Show file tree
Hide file tree
Showing 11 changed files with 178 additions and 161 deletions.
@@ -1,42 +1,28 @@
Implicit defaults
This test ensures that aria-orientation works correctly and the implicit defaults are defined on different roles.

Option
Menu item 1
menubar
radio 1
item one
tree item
cell
cell2
PASS: slider.orientation === 'AXOrientation: AXHorizontalOrientation'
PASS: combobox.orientation === 'AXOrientation: AXUnknownOrientation'
PASS: listbox.orientation === 'AXOrientation: AXVerticalOrientation'
PASS: menu.orientation === 'AXOrientation: AXVerticalOrientation'
PASS: menubar.orientation === 'AXOrientation: AXHorizontalOrientation'
PASS: radiogroup.orientation === 'AXOrientation: AXUnknownOrientation'
PASS: separator.orientation === 'AXOrientation: AXHorizontalOrientation'
PASS: tablist.orientation === 'AXOrientation: AXHorizontalOrientation'
PASS: toolbar.orientation === 'AXOrientation: AXHorizontalOrientation'
PASS: tree.orientation === 'AXOrientation: AXVerticalOrientation'
PASS: treegrid.orientation === 'AXOrientation: AXUnknownOrientation'
PASS: radiogroup2.orientation === 'AXOrientation: AXVerticalOrientation'
PASS: treegrid2.orientation === 'AXOrientation: AXHorizontalOrientation'
PASS: separator2.orientation === 'AXOrientation: AXUnknownOrientation'
PASS: slider2.orientation === 'AXOrientation: AXUnknownOrientation'
PASS: listbox2.orientation === 'AXOrientation: AXHorizontalOrientation'
PASS: listboxDisplayContents.orientation === 'AXOrientation: AXVerticalOrientation'

Authored orientation
radio 1
cell
cell2
X
Option
This test makes sure that aria-orientation works correctly and the implicit defaults are defined on different roles.
Updating #listbox-display-contents aria-orientation to horizontal.
PASS: listboxDisplayContents.orientation === 'AXOrientation: AXHorizontalOrientation'

On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".


PASS slider.orientation is 'AXOrientation: AXHorizontalOrientation'
PASS combobox.orientation is 'AXOrientation: AXUnknownOrientation'
PASS listbox.orientation is 'AXOrientation: AXVerticalOrientation'
PASS menu.orientation is 'AXOrientation: AXVerticalOrientation'
PASS menubar.orientation is 'AXOrientation: AXHorizontalOrientation'
PASS radiogroup.orientation is 'AXOrientation: AXUnknownOrientation'
PASS separator.orientation is 'AXOrientation: AXHorizontalOrientation'
PASS tablist.orientation is 'AXOrientation: AXHorizontalOrientation'
PASS toolbar.orientation is 'AXOrientation: AXHorizontalOrientation'
PASS tree.orientation is 'AXOrientation: AXVerticalOrientation'
PASS treegrid.orientation is 'AXOrientation: AXUnknownOrientation'
PASS radiogroup2.orientation is 'AXOrientation: AXVerticalOrientation'
PASS treegrid2.orientation is 'AXOrientation: AXHorizontalOrientation'
PASS separator2.orientation is 'AXOrientation: AXUnknownOrientation'
PASS slider2.orientation is 'AXOrientation: AXUnknownOrientation'
PASS listbox2.orientation is 'AXOrientation: AXHorizontalOrientation'
PASS successfullyParsed is true

TEST COMPLETE


@@ -1,114 +1,130 @@
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<html>
<head>
<script src="../resources/js-test-pre.js"></script>
<script src="../resources/accessibility-helper.js"></script>
<script src="../resources/js-test.js"></script>
</head>
<body id="body">
<body>

<div>Implicit defaults</div>
<input type="range" id="slider">
<input id="combo" type="text" role="combobox" placeholder="Placeholder" value="text">
<div id="listbox" role="listbox">
<div id="option1" role="option" aria-selected="true">Option</div>
</div>
<div id="menu" role="menu">
<div role="menuitem" id="item1" tabindex="0">Menu item 1</div>
</div>
<div id="menubar" role="menubar">menubar</div>
<div role="radiogroup" id="radiogroup">
<div role="radio" aria-label="radio 1" aria-checked="false">radio 1</div>
</div>
<div role="separator" id="separator"></div>
<div role="tablist" id="tablist">
<div role="toolbar" id="toolbar" aria-label="test toolbar"><li>item one</div>
<ul id="tree" role="tree">
<li id="treeitem1" role="treeitem">tree item</li>
</ul>
<table id="treegrid" role="treegrid">
<tbody>
<tr><td role="gridcell" colspan="3">cell</tr>
<tr><td role="gridcell" colspan="3">cell2</tr>
</tbody>
</table>

<br>
<div>Authored orientation</div>
<div role="radiogroup" id="radiogroup2" aria-orientation="vertical">
<div role="radio" aria-label="radio 1" aria-checked=false>radio 1</div>
</div>
<table id="treegrid2" role="treegrid" aria-orientation="horizontal">
<tbody>
<tr><td role="gridcell" colspan="3">cell</tr>
<tr><td role="gridcell" colspan="3">cell2</tr>
</tbody>
</table>
<div role="separator" id="separator2" aria-orientation="undefined"></div>
<span role="slider" id="slider2" aria-orientation="undefined" aria-valuenow=5 aria-valuemin=0 aria-valuemax=10>X</span>
<div role="listbox" id="listbox2" aria-orientation="horizontal">
<div id="option2" role="option" aria-selected="true">Option</div>
</div>
<div id="content">
<div>Implicit defaults</div>
<input type="range" id="slider">
<input id="combo" type="text" role="combobox" placeholder="Placeholder" value="text">
<div id="listbox" role="listbox">
<div id="option1" role="option" aria-selected="true">Option</div>
</div>
<div id="menu" role="menu">
<div role="menuitem" id="item1" tabindex="0">Menu item 1</div>
</div>
<div id="menubar" role="menubar">menubar</div>
<div role="radiogroup" id="radiogroup">
<div role="radio" aria-label="radio 1" aria-checked="false">radio 1</div>
</div>
<div role="separator" id="separator"></div>
<div role="tablist" id="tablist">
<div role="toolbar" id="toolbar" aria-label="test toolbar"><li>item one</div>
<ul id="tree" role="tree">
<li id="treeitem1" role="treeitem">tree item</li>
</ul>
<table id="treegrid" role="treegrid">
<tbody>
<tr><td role="gridcell" colspan="3">cell</tr>
<tr><td role="gridcell" colspan="3">cell2</tr>
</tbody>
</table>

<p id="description"></p>
<div id="console"></div>
<br>
<div>Authored orientation</div>
<div role="radiogroup" id="radiogroup2" aria-orientation="vertical">
<div role="radio" aria-label="radio 1" aria-checked=false>radio 1</div>
</div>
<table id="treegrid2" role="treegrid" aria-orientation="horizontal">
<tbody>
<tr><td role="gridcell" colspan="3">cell</tr>
<tr><td role="gridcell" colspan="3">cell2</tr>
</tbody>
</table>
<div role="separator" id="separator2" aria-orientation="undefined"></div>
<span role="slider" id="slider2" aria-orientation="undefined" aria-valuenow=5 aria-valuemin=0 aria-valuemax=10>X</span>
<div role="listbox" id="listbox2" aria-orientation="horizontal">
<div id="option2" role="option" aria-selected="true">Option</div>
</div>
<div role="listbox" id="listbox-display-contents" style="display:contents" aria-orientation="vertical">
<div id="option-within-display-contents" role="option" aria-selected="true">Option</div>
</div>
</div>

<script>

description("This test makes sure that aria-orientation works correctly and the implicit defaults are defined on different roles.");
var testOutput = "This test ensures that aria-orientation works correctly and the implicit defaults are defined on different roles.\n\n";

if (window.accessibilityController) {
// Test implicit defaults on some roles.
var slider = accessibilityController.accessibleElementById("slider");
shouldBe("slider.orientation", "'AXOrientation: AXHorizontalOrientation'");

var combobox = accessibilityController.accessibleElementById("combo");
shouldBe("combobox.orientation", "'AXOrientation: AXUnknownOrientation'");

var listbox = accessibilityController.accessibleElementById("listbox");
shouldBe("listbox.orientation", "'AXOrientation: AXVerticalOrientation'");

var menu = accessibilityController.accessibleElementById("menu");
shouldBe("menu.orientation", "'AXOrientation: AXVerticalOrientation'");

var menubar = accessibilityController.accessibleElementById("menubar");
shouldBe("menubar.orientation", "'AXOrientation: AXHorizontalOrientation'");

var radiogroup = accessibilityController.accessibleElementById("radiogroup");
shouldBe("radiogroup.orientation", "'AXOrientation: AXUnknownOrientation'");

var separator = accessibilityController.accessibleElementById("separator");
shouldBe("separator.orientation", "'AXOrientation: AXHorizontalOrientation'");

var tablist = accessibilityController.accessibleElementById("tablist");
shouldBe("tablist.orientation", "'AXOrientation: AXHorizontalOrientation'");

var toolbar = accessibilityController.accessibleElementById("toolbar");
shouldBe("toolbar.orientation", "'AXOrientation: AXHorizontalOrientation'");

var tree = accessibilityController.accessibleElementById("tree");
shouldBe("tree.orientation", "'AXOrientation: AXVerticalOrientation'");

var treegrid = accessibilityController.accessibleElementById("treegrid");
shouldBe("treegrid.orientation", "'AXOrientation: AXUnknownOrientation'");

// Test authored aria-orientation are correct.
var radiogroup2 = accessibilityController.accessibleElementById("radiogroup2");
shouldBe("radiogroup2.orientation", "'AXOrientation: AXVerticalOrientation'");

var treegrid2 = accessibilityController.accessibleElementById("treegrid2");
shouldBe("treegrid2.orientation", "'AXOrientation: AXHorizontalOrientation'");

var separator2 = accessibilityController.accessibleElementById("separator2");
shouldBe("separator2.orientation", "'AXOrientation: AXUnknownOrientation'");

var slider2 = accessibilityController.accessibleElementById("slider2");
shouldBe("slider2.orientation", "'AXOrientation: AXUnknownOrientation'");

var listbox2 = accessibilityController.accessibleElementById("listbox2");
shouldBe("listbox2.orientation", "'AXOrientation: AXHorizontalOrientation'");
}
window.jsTestIsAsync = true;

</script>
// Test implicit defaults on some roles.
var slider = accessibilityController.accessibleElementById("slider");
testOutput += expect("slider.orientation", "'AXOrientation: AXHorizontalOrientation'");

var combobox = accessibilityController.accessibleElementById("combo");
testOutput += expect("combobox.orientation", "'AXOrientation: AXUnknownOrientation'");

var listbox = accessibilityController.accessibleElementById("listbox");
testOutput += expect("listbox.orientation", "'AXOrientation: AXVerticalOrientation'");

var menu = accessibilityController.accessibleElementById("menu");
testOutput += expect("menu.orientation", "'AXOrientation: AXVerticalOrientation'");

var menubar = accessibilityController.accessibleElementById("menubar");
testOutput += expect("menubar.orientation", "'AXOrientation: AXHorizontalOrientation'");

var radiogroup = accessibilityController.accessibleElementById("radiogroup");
testOutput += expect("radiogroup.orientation", "'AXOrientation: AXUnknownOrientation'");

var separator = accessibilityController.accessibleElementById("separator");
testOutput += expect("separator.orientation", "'AXOrientation: AXHorizontalOrientation'");

<script src="../resources/js-test-post.js"></script>
var tablist = accessibilityController.accessibleElementById("tablist");
testOutput += expect("tablist.orientation", "'AXOrientation: AXHorizontalOrientation'");

var toolbar = accessibilityController.accessibleElementById("toolbar");
testOutput += expect("toolbar.orientation", "'AXOrientation: AXHorizontalOrientation'");

var tree = accessibilityController.accessibleElementById("tree");
testOutput += expect("tree.orientation", "'AXOrientation: AXVerticalOrientation'");

var treegrid = accessibilityController.accessibleElementById("treegrid");
testOutput += expect("treegrid.orientation", "'AXOrientation: AXUnknownOrientation'");

// Test authored aria-orientation are correct.
var radiogroup2 = accessibilityController.accessibleElementById("radiogroup2");
testOutput += expect("radiogroup2.orientation", "'AXOrientation: AXVerticalOrientation'");

var treegrid2 = accessibilityController.accessibleElementById("treegrid2");
testOutput += expect("treegrid2.orientation", "'AXOrientation: AXHorizontalOrientation'");

var separator2 = accessibilityController.accessibleElementById("separator2");
testOutput += expect("separator2.orientation", "'AXOrientation: AXUnknownOrientation'");

var slider2 = accessibilityController.accessibleElementById("slider2");
testOutput += expect("slider2.orientation", "'AXOrientation: AXUnknownOrientation'");

var listbox2 = accessibilityController.accessibleElementById("listbox2");
testOutput += expect("listbox2.orientation", "'AXOrientation: AXHorizontalOrientation'");

var listboxDisplayContents = accessibilityController.accessibleElementById("listbox-display-contents");
testOutput += expect("listboxDisplayContents.orientation", "'AXOrientation: AXVerticalOrientation'");

testOutput += "\nUpdating #listbox-display-contents aria-orientation to horizontal.\n"
document.getElementById("listbox-display-contents").setAttribute("aria-orientation", "horizontal");
setTimeout(async function() {
await waitFor(() => listboxDisplayContents.orientation.includes("AXHorizontalOrientation"));
testOutput += expect("listboxDisplayContents.orientation", "'AXOrientation: AXHorizontalOrientation'");

document.getElementById("content").style.visibility = "hidden";
debug(testOutput);
finishJSTest();
}, 0);
}
</script>
</body>
</html>

@@ -433,6 +433,9 @@ TextStream& operator<<(TextStream& stream, AXObjectCache::AXNotification notific
case AXObjectCache::AXNotification::AXNewDocumentLoadComplete:
stream << "AXNewDocumentLoadComplete";
break;
case AXObjectCache::AXNotification::AXOrientationChanged:
stream << "AXOrientationChanged";
break;
case AXObjectCache::AXNotification::AXPageScrolled:
stream << "AXPageScrolled";
break;
@@ -1971,6 +1971,8 @@ void AXObjectCache::handleAttributeChange(const QualifiedName& attrName, Element
}
else if (attrName == aria_multiselectableAttr)
postNotification(element, AXMultiSelectableStateChanged);
else if (attrName == aria_orientationAttr)
postNotification(element, AXOrientationChanged);
else if (attrName == aria_posinsetAttr)
postNotification(element, AXPositionInSetChanged);
else if (attrName == aria_relevantAttr)
@@ -3535,6 +3537,9 @@ void AXObjectCache::updateIsolatedTree(const Vector<std::pair<RefPtr<AXCoreObjec
tree->updateNodeProperty(*notification.first, AXPropertyName::MinValueForRange);
tree->updateNodeProperty(*notification.first, AXPropertyName::ValueForRange);
break;
case AXOrientationChanged:
tree->updateNodeProperty(*notification.first, AXPropertyName::Orientation);
break;
case AXPositionInSetChanged:
tree->updateNodeProperty(*notification.first, AXPropertyName::PosInSet);
tree->updateNodeProperty(*notification.first, AXPropertyName::SupportsPosInSet);
@@ -313,6 +313,7 @@ class AXObjectCache {
AXMenuOpened,
AXMinimumValueChanged,
AXMultiSelectableStateChanged,
AXOrientationChanged,
AXRowCountChanged,
AXRowCollapsed,
AXRowExpanded,
@@ -980,6 +980,31 @@ float AccessibilityNodeObject::stepValueForRange() const
return getAttribute(stepAttr).toFloat();
}

AccessibilityOrientation AccessibilityNodeObject::orientation() const
{
const AtomString& ariaOrientation = getAttribute(aria_orientationAttr);
if (equalLettersIgnoringASCIICase(ariaOrientation, "horizontal"_s))
return AccessibilityOrientation::Horizontal;
if (equalLettersIgnoringASCIICase(ariaOrientation, "vertical"_s))
return AccessibilityOrientation::Vertical;
if (equalLettersIgnoringASCIICase(ariaOrientation, "undefined"_s))
return AccessibilityOrientation::Undefined;

// In ARIA 1.1, the implicit value of aria-orientation changed from horizontal
// to undefined on all roles that don't have their own role-specific values. In
// addition, the implicit value of combobox became undefined.
if (isComboBox() || isRadioGroup() || isTreeGrid())
return AccessibilityOrientation::Undefined;

if (isScrollbar() || isListBox() || isMenu() || isTree())
return AccessibilityOrientation::Vertical;

if (isMenuBar() || isSplitter() || isTabList() || isToolbar() || isSlider())
return AccessibilityOrientation::Horizontal;

return AccessibilityObject::orientation();
}

bool AccessibilityNodeObject::isHeading() const
{
return roleValue() == AccessibilityRole::Heading;
@@ -101,6 +101,8 @@ class AccessibilityNodeObject : public AccessibilityObject {
float minValueForRange() const override;
float stepValueForRange() const override;

AccessibilityOrientation orientation() const override;

AXCoreObject* selectedRadioButton() override;
AXCoreObject* selectedTabItem() override;
AccessibilityButtonState checkboxOrRadioValue() const override;

0 comments on commit 199b01f

Please sign in to comment.