Skip to content
Permalink
Browse files
Integrate ARIA element reflection in the Accessibility Tree
https://bugs.webkit.org/show_bug.cgi?id=244972

Reviewed by Ryosuke Niwa.

* LayoutTests/accessibility/element-reflection-ariaactivedescendant-expected.txt: Added.
* LayoutTests/accessibility/element-reflection-ariaactivedescendant.html: Added.
* LayoutTests/accessibility/element-reflection-ariacontrols-expected.txt: Added.
* LayoutTests/accessibility/element-reflection-ariacontrols.html: Added.
* LayoutTests/accessibility/element-reflection-ariadescribedby-expected.txt: Added.
* LayoutTests/accessibility/element-reflection-ariadescribedby.html: Added.
* LayoutTests/accessibility/element-reflection-ariadetails-expected.txt: Added.
* LayoutTests/accessibility/element-reflection-ariadetails.html: Added.
* LayoutTests/accessibility/element-reflection-ariaerrormessage-expected.txt: Added.
* LayoutTests/accessibility/element-reflection-ariaerrormessage.html: Added.
* LayoutTests/accessibility/element-reflection-ariaflowto-expected.txt: Added.
* LayoutTests/accessibility/element-reflection-ariaflowto.html: Added.
* LayoutTests/accessibility/element-reflection-arialabelledby-expected.txt: Added.
* LayoutTests/accessibility/element-reflection-arialabelledby.html: Added.
* LayoutTests/accessibility/element-reflection-ariaowns-expected.txt: Added.
* LayoutTests/accessibility/element-reflection-ariaowns.html: Added.
* LayoutTests/platform/mac-wk1/TestExpectations: Skip tests due to
  missing methods on WK1.
* LayoutTests/platform/win/TestExpectations: Skip new tests on Windows.
* Source/WebCore/accessibility/AXObjectCache.cpp:
(WebCore::AXObjectCache::updateRelationsIfNeeded): Take into account
 element reflection information.
* Source/WebCore/accessibility/AccessibilityObject.cpp:
(WebCore:: const): Ditto.
* Source/WebCore/dom/Element.cpp:
(WebCore::Element::isElementReflectionAttribute): Expose method so
 it can be used from the a11y code.
(WebCore::Element::isElementsArrayReflectionAttribute): Ditto.
(WebCore::isElementReflectionAttribute): Deleted.
(WebCore::isElementsArrayReflectionAttribute): Deleted.
* Source/WebCore/dom/Element.h:

Canonical link: https://commits.webkit.org/254905@main
  • Loading branch information
mrego committed Sep 27, 2022
1 parent 89c0e39 commit 5a61ea9a825740c42f60af419813acb143df91f7
Show file tree
Hide file tree
Showing 22 changed files with 834 additions and 7 deletions.
@@ -0,0 +1,17 @@
Checks that element reflection is exposed to the a11y tree for 'ariaActiveDescendantElement'

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


PASS axTarget1.selectedChildrenCount is 1
PASS axTarget1.selectedChildAtIndex(0).isEqual(axDescendant1) is true
PASS axTarget2.selectedChildrenCount is 1
PASS axTarget2.selectedChildAtIndex(0).isEqual(axDescendantFirstChild) is true
PASS axTarget2.selectedChildrenCount is 1
PASS axTarget2.selectedChildAtIndex(0).isEqual(axDescendant2) is true
PASS axTarget3.selectedChildrenCount is 1
PASS axTarget3.selectedChildAtIndex(0).isEqual(axDescendant3) is true
PASS successfullyParsed is true

TEST COMPLETE

@@ -0,0 +1,43 @@
<!DOCTYPE html>
<script src="../resources/js-test.js"></script>
<script src="../resources/accessibility-helper.js"></script>
<ul id="target1" role="menubar">
<li id="descendant1"></li>
</ul>
<ul id="target2" role="menubar">
<li></li>
<li id="descendant2"></li>
</ul>
<ul id="target3" role="menubar">
<li id="descendant3"></li>
</ul>

<script>
description("Checks that element reflection is exposed to the a11y tree for 'ariaActiveDescendantElement'");
if (!window.accessibilityController) {
debug("This test requires accessibilityController");
} else {
target1.ariaActiveDescendantElement = descendant1;
var axTarget1 = accessibilityController.accessibleElementById("target1");
var axDescendant1 = accessibilityController.accessibleElementById("descendant1");
shouldBe("axTarget1.selectedChildrenCount", "1");
shouldBeTrue("axTarget1.selectedChildAtIndex(0).isEqual(axDescendant1)");

target2.ariaActiveDescendantElement = target2.firstElementChild;
var axTarget2 = accessibilityController.accessibleElementById("target2");
var axDescendant2 = accessibilityController.accessibleElementById("descendant2");
var axDescendantFirstChild = axTarget2.childAtIndex(0);
shouldBe("axTarget2.selectedChildrenCount", "1");
shouldBeTrue("axTarget2.selectedChildAtIndex(0).isEqual(axDescendantFirstChild)");
target2.setAttribute("aria-activedescendant", "descendant2");
shouldBe("axTarget2.selectedChildrenCount", "1");
shouldBeTrue("axTarget2.selectedChildAtIndex(0).isEqual(axDescendant2)");

target3.ariaActiveDescendantElement = descendant3;
descendant3.id = "descendant3-new";
var axTarget3 = accessibilityController.accessibleElementById("target3");
var axDescendant3 = accessibilityController.accessibleElementById("descendant3-new");
shouldBe("axTarget3.selectedChildrenCount", "1");
shouldBeTrue("axTarget3.selectedChildAtIndex(0).isEqual(axDescendant3)");
}
</script>
@@ -0,0 +1,25 @@
Checks that element reflection is exposed to the a11y tree for 'ariaControlsElements'

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


PASS axTab1.ariaControlsElementAtIndex(0).isEqual(axPanel1) is true
PASS axTab2.ariaControlsElementAtIndex(0).isEqual(axPanel2) is true
PASS axTab2.ariaControlsElementAtIndex(0).isEqual(axPanel1) is true
PASS axTab3.ariaControlsElementAtIndex(0).isEqual(axPanel3) is true
PASS axTab2.ariaControlsElementAtIndex(0).isEqual(axPanel1) is true
PASS axTab2.ariaControlsElementAtIndex(1).isEqual(axPanel2) is true
PASS axTab2.ariaControlsElementAtIndex(2).isEqual(axPanel3) is true
PASS axTab4.ariaControlsElementAtIndex(0).isEqual(axPanel4) is true
PASS axTab5.ariaControlsElementAtIndex(0).isEqual(axPanel5) is true
PASS successfullyParsed is true

TEST COMPLETE
First panel
Tab 1
Second panel
Tab 2
Third panel
Fourth panel
Tab 4
Fifth panel
@@ -0,0 +1,86 @@
<!DOCTYPE html>
<script src="../resources/js-test.js"></script>
<script src="../resources/accessibility-helper.js"></script>
<div id="panel1">First panel</div>
<div id="tab1" role="tab">Tab 1</div>
<div id="wrapper" tabindex="0">
<div class="panel">Second panel</div>
</div>
<div id="tab2" role="tab">Tab 2</div>
<div id="panel3">Third panel</div>
<x-tab></x-tab>
<div id="panel4">Fourth panel</div>
<div id="tab4" role="tab">Tab 4</div>
<x-custom></x-custom>

<script>
class XTab extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: "open" });
let tab = document.createElement("div");
tab.id = "innertab";
tab.role = "tab";
tab.textContent = "Tab 3";
tab.ariaControlsElements = [panel3];
this.shadowRoot.appendChild(tab);
}
}
customElements.define("x-tab", XTab);

class XCustom extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: "open" });
let panel = document.createElement("div");
panel.id = "panel5";
panel.textContent = "Fifth panel";
let tab = document.createElement("div");
tab.id = "tab5";
tab.role = "tab";
tab.textContent = "Tab 5";
this.shadowRoot.appendChild(panel);
this.shadowRoot.appendChild(tab);
tab.ariaControlsElements = [panel];
document.body.appendChild(panel);
}
}
customElements.define("x-custom", XCustom);

description("Checks that element reflection is exposed to the a11y tree for 'ariaControlsElements'");
if (!window.accessibilityController) {
debug("This test requires accessibilityController");
} else {
tab1.ariaControlsElements = [panel1];
var axPanel1 = accessibilityController.accessibleElementById("panel1");
var axTab1 = accessibilityController.accessibleElementById("tab1");
shouldBeTrue("axTab1.ariaControlsElementAtIndex(0).isEqual(axPanel1)");

tab2.ariaControlsElements = [document.getElementsByClassName("panel")[0]];
var wrapper = accessibilityController.accessibleElementById("wrapper");
var axPanel2 = wrapper.childAtIndex(0);
var axTab2 = accessibilityController.accessibleElementById("tab2");
shouldBeTrue("axTab2.ariaControlsElementAtIndex(0).isEqual(axPanel2)");
tab2.setAttribute("aria-controls", "panel1");
shouldBeTrue("axTab2.ariaControlsElementAtIndex(0).isEqual(axPanel1)");

var axPanel3 = accessibilityController.accessibleElementById("panel3");
var axTab3 = accessibilityController.accessibleElementById("innertab");
shouldBeTrue("axTab3.ariaControlsElementAtIndex(0).isEqual(axPanel3)");

tab2.ariaControlsElements = [panel1, document.getElementsByClassName("panel")[0], panel3];
shouldBeTrue("axTab2.ariaControlsElementAtIndex(0).isEqual(axPanel1)");
shouldBeTrue("axTab2.ariaControlsElementAtIndex(1).isEqual(axPanel2)");
shouldBeTrue("axTab2.ariaControlsElementAtIndex(2).isEqual(axPanel3)");

tab4.ariaControlsElements = [panel4];
panel4.id = "panel4-new";
var axPanel4 = accessibilityController.accessibleElementById("panel4-new");
var axTab4 = accessibilityController.accessibleElementById("tab4");
shouldBeTrue("axTab4.ariaControlsElementAtIndex(0).isEqual(axPanel4)");

var axPanel5 = accessibilityController.accessibleElementById("panel5");
var axTab5 = accessibilityController.accessibleElementById("tab5");
shouldBeTrue("axTab5.ariaControlsElementAtIndex(0).isEqual(axPanel5)");
}
</script>
@@ -0,0 +1,23 @@
Checks that element reflection is exposed to the a11y tree for 'ariaDescribedByElements'

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


PASS axTarget1.helpText is "AXHelp: First description"
PASS axTarget2.helpText is "AXHelp: Second description"
PASS axTarget2.helpText is "AXHelp: First description"
PASS axInnerTarget.helpText is "AXHelp: Third description"
PASS axTarget2.helpText is "AXHelp: First description Second description Third description"
PASS axTarget4.helpText is "AXHelp: Fourth description"
PASS axTarget5.helpText is "AXHelp: Fifth description"
PASS successfullyParsed is true

TEST COMPLETE
First description
Target 1
Second description
Target 2
Third description
Fourth description
Target 4
Fifth description
@@ -0,0 +1,75 @@
<!DOCTYPE html>
<script src="../resources/js-test.js"></script>
<script src="../resources/accessibility-helper.js"></script>

<div id="desc1">First description</div>
<div id="target1">Target 1</div>
<div class="desc">Second description</div>
<div id="target2">Target 2</div>
<div id="desc3">Third description</div>
<x-target></x-target>
<div id="desc4">Fourth description</div>
<div id="target4">Target 4</div>
<x-custom></x-custom>

<script>
class XTarget extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: "open" });
let target = document.createElement("div");
target.id = "innertarget";
target.textContent = "Target 3";
target.ariaDescribedByElements = [desc3];
this.shadowRoot.appendChild(target);
}
}
customElements.define("x-target", XTarget);

class XCustom extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: "open" });
let desc = document.createElement("div");
desc.id = "desc5";
desc.textContent = "Fifth description";
let target = document.createElement("div");
target.id = "target5";
target.textContent = "Target 5";
this.shadowRoot.appendChild(desc);
this.shadowRoot.appendChild(target);
target.ariaDescribedByElements = [desc];
document.body.appendChild(desc);
}
}
customElements.define("x-custom", XCustom);

description("Checks that element reflection is exposed to the a11y tree for 'ariaDescribedByElements'");
if (!window.accessibilityController) {
debug("This test requires accessibilityController");
} else {
target1.ariaDescribedByElements = [desc1];
var axTarget1 = accessibilityController.accessibleElementById("target1");
shouldBeEqualToString("axTarget1.helpText", "AXHelp: First description");

target2.ariaDescribedByElements = [document.getElementsByClassName("desc")[0]];
var axTarget2 = accessibilityController.accessibleElementById("target2");
shouldBeEqualToString("axTarget2.helpText", "AXHelp: Second description");
target2.setAttribute("aria-describedby", "desc1");
shouldBeEqualToString("axTarget2.helpText", "AXHelp: First description");

var axInnerTarget = accessibilityController.accessibleElementById("innertarget");
shouldBeEqualToString("axInnerTarget.helpText", "AXHelp: Third description");

target2.ariaDescribedByElements = [desc1, document.getElementsByClassName("desc")[0], desc3];
shouldBeEqualToString("axTarget2.helpText", "AXHelp: First description Second description Third description");

target4.ariaDescribedByElements = [desc4];
desc4.id = "desc4-new";
var axTarget4 = accessibilityController.accessibleElementById("target4");
shouldBeEqualToString("axTarget4.helpText", "AXHelp: Fourth description");

var axTarget5 = accessibilityController.accessibleElementById("target5");
shouldBeEqualToString("axTarget5.helpText", "AXHelp: Fifth description");
}
</script>
@@ -0,0 +1,25 @@
Checks that element reflection is exposed to the a11y tree for 'ariaDetailsElements'

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


PASS axTarget1.ariaDetailsElementAtIndex(0).isEqual(axDetail1) is true
PASS axTarget2.ariaDetailsElementAtIndex(0).isEqual(axDetail2) is true
PASS axTarget2.ariaDetailsElementAtIndex(0).isEqual(axDetail1) is true
PASS axInnerTarget.ariaDetailsElementAtIndex(0).isEqual(axDetail3) is true
PASS axTarget2.ariaDetailsElementAtIndex(0).isEqual(axDetail1) is true
PASS axTarget2.ariaDetailsElementAtIndex(1).isEqual(axDetail2) is true
PASS axTarget2.ariaDetailsElementAtIndex(2).isEqual(axDetail3) is true
PASS axTarget4.ariaDetailsElementAtIndex(0).isEqual(axDetail4) is true
PASS axTarget5.ariaDetailsElementAtIndex(0).isEqual(axDetail5) is true
PASS successfullyParsed is true

TEST COMPLETE
First detail
Target 1
Second detail
Target 2
Third detail
Fourth detail
Target 4
Fifth detail
@@ -0,0 +1,84 @@
<!DOCTYPE html>
<script src="../resources/js-test.js"></script>
<script src="../resources/accessibility-helper.js"></script>
<div id="detail1">First detail</div>
<div id="target1">Target 1</div>
<div id="wrapper" tabindex="0">
<div class="detail">Second detail</div>
</div>
<div id="target2">Target 2</div>
<div id="detail3">Third detail</div>
<x-target></x-target>
<div id="detail4">Fourth detail</div>
<div id="target4">Target 4</div>
<x-custom></x-custom>

<script>
class XTarget extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: "open" });
let target = document.createElement("div");
target.id = "innertarget";
target.textContent = "Target 3";
target.ariaDetailsElements = [detail3];
this.shadowRoot.appendChild(target);
}
}
customElements.define("x-target", XTarget);

class XCustom extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: "open" });
let detail = document.createElement("div");
detail.id = "detail5";
detail.textContent = "Fifth detail";
let target = document.createElement("div");
target.id = "target5";
target.textContent = "Target 5";
this.shadowRoot.appendChild(detail);
this.shadowRoot.appendChild(target);
target.ariaDetailsElements = [detail];
document.body.appendChild(detail);
}
}
customElements.define("x-custom", XCustom);

description("Checks that element reflection is exposed to the a11y tree for 'ariaDetailsElements'");
if (!window.accessibilityController) {
debug("This test requires accessibilityController");
} else {
target1.ariaDetailsElements = [detail1];
var axDetail1 = accessibilityController.accessibleElementById("detail1");
var axTarget1 = accessibilityController.accessibleElementById("target1");
shouldBeTrue("axTarget1.ariaDetailsElementAtIndex(0).isEqual(axDetail1)");

target2.ariaDetailsElements = [document.getElementsByClassName("detail")[0]];
var wrapper = accessibilityController.accessibleElementById("wrapper");
var axDetail2 = wrapper.childAtIndex(0);
var axTarget2 = accessibilityController.accessibleElementById("target2");
shouldBeTrue("axTarget2.ariaDetailsElementAtIndex(0).isEqual(axDetail2)");
target2.setAttribute("aria-details", "detail1");
shouldBeTrue("axTarget2.ariaDetailsElementAtIndex(0).isEqual(axDetail1)");

var axDetail3 = accessibilityController.accessibleElementById("detail3");
var axInnerTarget = accessibilityController.accessibleElementById("innertarget");
shouldBeTrue("axInnerTarget.ariaDetailsElementAtIndex(0).isEqual(axDetail3)");

target2.ariaDetailsElements = [detail1, document.getElementsByClassName("detail")[0], detail3];
shouldBeTrue("axTarget2.ariaDetailsElementAtIndex(0).isEqual(axDetail1)");
shouldBeTrue("axTarget2.ariaDetailsElementAtIndex(1).isEqual(axDetail2)");
shouldBeTrue("axTarget2.ariaDetailsElementAtIndex(2).isEqual(axDetail3)");

target4.ariaDetailsElements = [detail4];
detail4.id = "detail4-new";
var axDetail4 = accessibilityController.accessibleElementById("detail4-new");
var axTarget4 = accessibilityController.accessibleElementById("target4");
shouldBeTrue("axTarget4.ariaDetailsElementAtIndex(0).isEqual(axDetail4)");

var axDetail5 = accessibilityController.accessibleElementById("detail5");
var axTarget5 = accessibilityController.accessibleElementById("target5");
shouldBeTrue("axTarget5.ariaDetailsElementAtIndex(0).isEqual(axDetail5)");
}
</script>

0 comments on commit 5a61ea9

Please sign in to comment.