1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227
|
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Element Reflection for aria-activedescendant and aria-errormessage on ElementInternals</title>
<link rel=help href="https://whatpr.org/html/3917/common-dom-interfaces.html#reflecting-content-attributes-in-idl-attributes:element">
<link rel="author" title="Alice Boxhall" href="alice@igalia.com">
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/resources/testdriver-actions.js"></script>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
</head>
<body>
<script>
class CustomElement extends HTMLElement {
constructor() {
super();
this.i = this.attachInternals();
}
}
customElements.define('custom-element', CustomElement);
</script>
<custom-element id="custom1"></custom-element>
<div id="activedescendant">Active descendant</div>
<div id="controls">Controls</div>
<div id="describedby">Described by</div>
<div id="details">Details</div>
<div id="errormessage">Error message</div>
<div id="flowto">Flow to</div>
<div id="labelledby">Labelled by</div>
<div id="owns">Owns</div>
<div id="labelledby_content">Labelled by from content attribute</div>
<div id="labelledby_element">Labelled by from IDL attribute</div>
<div id="shadowhost">
<template shadowrootmode="open">
<div id="labelledby_element_shadow">Invalid labelled by in shadow root</div>
</template>
</div>
<script>
const element_properties = [
['ariaActiveDescendantElement', 'activedescendant']];
const array_properties = [
['ariaControlsElements', 'controls'],
['ariaDescribedByElements', 'describedby'],
['ariaDetailsElements', 'details'],
['ariaErrorMessageElements', 'errormessage'],
['ariaFlowToElements', 'flowto'],
['ariaLabelledByElements', 'labelledby'],
['ariaOwnsElements', 'owns']];
test(t => {
const custom = document.getElementById('custom1');
for (const [property, id] of element_properties) {
assert_equals(custom.i[property], null);
}
for (const [property, id] of array_properties) {
assert_equals(custom.i[property], null);
}
}, "Getting previously-unset ARIA element reflection properties on ElementInternals should return null.");
test(t => {
const custom = document.getElementById('custom1');
for (const [property, id] of element_properties) {
const related = document.getElementById(id);
custom.i[property] = related;
assert_equals(custom.i[property], related);
}
for (const [property, id] of array_properties) {
const related = document.getElementById(id);
custom.i[property] = [related];
assert_array_equals(custom.i[property], [related]);
}
}, "Getting ARIA element reflection properties on ElementInternals should return the value that was set.");
test(t => {
const custom = document.getElementById('custom1');
for (const [property, id] of array_properties) {
custom.i[property] = [];
assert_array_equals(custom.i[property], []);
}
}, "Setting ARIA element reflection properties to an empty array should work as expected.");
test(t => {
const custom = document.getElementById('custom1');
for (const [property, id] of element_properties) {
const related = document.getElementById(id);
custom.i[property] = related;
assert_equals(custom.i[property], related);
custom.i[property] = null;
assert_equals(custom.i[property], null);
}
for (const [property, id] of array_properties) {
const related = document.getElementById(id);
custom.i[property] = [related];
assert_array_equals(custom.i[property], [related]);
custom.i[property] = null;
assert_equals(custom.i[property], null);
}
}, "Setting ARIA element reflection properties on ElementInternals to null should delete any previous value, and not crash");
promise_test(async t => {
const custom = document.getElementById('custom1');
custom.i.ariaLabelledByElements = null;
const label_before_labelledby_set = await test_driver.get_computed_label(custom);
assert_equals(label_before_labelledby_set, "", "Before ariaLabelledByElements is set, accessible label should be empty.");
const labelledBy = document.getElementById('labelledby');
custom.i.ariaLabelledByElements = [labelledBy];
const label_after_labelledby_set = await test_driver.get_computed_label(custom);
assert_equals(label_after_labelledby_set, "Labelled by", "After ariaLabelledByElements is set, accessible label should be 'Labelled by'");
}, "Setting ariaLabelledByElements on ElementInternals should change the accessible name of the custom element")
promise_test(async t => {
const custom = document.getElementById('custom1');
custom.i.ariaLabelledByElements = null;
const label_before_labelledby_set = await test_driver.get_computed_label(custom);
assert_equals(label_before_labelledby_set, "", "Before ariaLabelledByElements is set, accessible label should be empty.");
const labelledBy = document.getElementById('labelledby');
custom.i.ariaLabelledByElements = [labelledBy];
const label_after_internals_labelledby_set = await test_driver.get_computed_label(custom);
assert_equals(label_after_internals_labelledby_set, "Labelled by", "After ariaLabelledByElements is set, accessible label should be 'Labelled by'");
custom.setAttribute('aria-labelledby', 'labelledby_content');
const label_after_content_labelledby_set = await test_driver.get_computed_label(custom);
assert_equals(label_after_content_labelledby_set, "Labelled by from content attribute", "aria-labelledby content attribute supersedes ariaLabelledByElements on internals");
const labelledby_element = document.getElementById('labelledby_element');
custom.ariaLabelledByElements = [labelledby_element];
const label_after_idl_arialabelledbyelements_set = await test_driver.get_computed_label(custom);
assert_equals(label_after_idl_arialabelledbyelements_set, "Labelled by from IDL attribute", "ariaLabelledByElements on element supersedes ariaLabelledByElements on internals");
custom.setAttribute('aria-labelledby', 'bad_id');
const label_after_content_labelledby_set_to_bad_id = await test_driver.get_computed_label(custom);
assert_equals(label_after_content_labelledby_set_to_bad_id, "", "aria-labelledby content attribute supersedes ariaLabelledByElements on internals even when invalid");
const shadowhost = document.getElementById('shadowhost');
const labelledby_element_invalid = shadowhost.shadowRoot.getElementById('labelledby_element_shadow');
custom.ariaLabelledByElements = [labelledby_element_invalid];
const label_after_idl_arialabelledbyelements_set_to_invalid_element = await test_driver.get_computed_label(custom);
assert_equals(label_after_idl_arialabelledbyelements_set_to_invalid_element, "", "ariaLabelledByElements on element supersedes ariaLabelledByElements on internals even when invalid");
}, "Setting aria-labelledby or ariaLabelledByElements on the custom element should supersede the value of ariaLabelledByElements on ElementInternals");
</script>
<custom-element id="cachingInvariantMain"></custom-element>
<div id="cachingInvariantElement1"></div>
<div id="cachingInvariantElement2"></div>
<div id="cachingInvariantElement3"></div>
<div id="cachingInvariantElement4"></div>
<div id="cachingInvariantElement5"></div>
<script>
test(function(t) {
cachingInvariantMain.i.ariaControlsElements = [cachingInvariantElement1, cachingInvariantElement2];
cachingInvariantMain.i.ariaDescribedByElements = [cachingInvariantElement3, cachingInvariantElement4];
cachingInvariantMain.i.ariaDetailsElements = [cachingInvariantElement5];
cachingInvariantMain.i.ariaFlowToElements = [cachingInvariantElement1, cachingInvariantElement3];
cachingInvariantMain.i.ariaLabelledByElements = [cachingInvariantElement2, cachingInvariantElement4];
cachingInvariantMain.i.ariaOwnsElements = [cachingInvariantElement1, cachingInvariantElement2, cachingInvariantElement3];
let ariaControlsElementsArray = cachingInvariantMain.i.ariaControlsElements;
let ariaDescribedByElementsArray = cachingInvariantMain.i.ariaDescribedByElements;
let ariaDetailsElementsArray = cachingInvariantMain.i.ariaDetailsElements;
let ariaFlowToElementsArray = cachingInvariantMain.i.ariaFlowToElements;
let ariaLabelledByElementsArray = cachingInvariantMain.i.ariaLabelledByElements;
let ariaOwnsElementsArray = cachingInvariantMain.i.ariaOwnsElements;
assert_equals(ariaControlsElementsArray, cachingInvariantMain.i.ariaControlsElements, "Caching invariant for ariaControlsElements");
assert_equals(ariaDescribedByElementsArray, cachingInvariantMain.i.ariaDescribedByElements, "Caching invariant for ariaDescribedByElements");
assert_equals(ariaDetailsElementsArray, cachingInvariantMain.i.ariaDetailsElements, "Caching invariant for ariaDetailsElements");
assert_equals(ariaFlowToElementsArray, cachingInvariantMain.i.ariaFlowToElements, "Caching invariant for ariaFlowToElements");
assert_equals(ariaLabelledByElementsArray, cachingInvariantMain.i.ariaLabelledByElements, "Caching invariant for ariaLabelledByElements");
assert_equals(ariaOwnsElementsArray, cachingInvariantMain.i.ariaOwnsElements, "Caching invariant for ariaOwnsElements");
// Ensure that cached values don't become stale
cachingInvariantMain.i.ariaControlsElements = [cachingInvariantElement4, cachingInvariantElement5];
cachingInvariantMain.i.ariaDescribedByElements = [cachingInvariantElement1, cachingInvariantElement2];
cachingInvariantMain.i.ariaDetailsElements = [cachingInvariantElement3];
cachingInvariantMain.i.ariaFlowToElements = [cachingInvariantElement4, cachingInvariantElement5];
cachingInvariantMain.i.ariaLabelledByElements = [cachingInvariantElement1, cachingInvariantElement2];
cachingInvariantMain.i.ariaOwnsElements = [cachingInvariantElement3, cachingInvariantElement4, cachingInvariantElement1];
ariaControlsElementsArray = cachingInvariantMain.i.ariaControlsElements;
ariaDescribedByElementsArray = cachingInvariantMain.i.ariaDescribedByElements;
ariaDetailsElementsArray = cachingInvariantMain.i.ariaDetailsElements;
ariaFlowToElementsArray = cachingInvariantMain.i.ariaFlowToElements;
ariaLabelledByElementsArray = cachingInvariantMain.i.ariaLabelledByElements;
ariaOwnsElementsArray = cachingInvariantMain.i.ariaOwnsElements;
assert_equals(ariaControlsElementsArray, cachingInvariantMain.i.ariaControlsElements, "Caching invariant for ariaControlsElements");
assert_equals(ariaDescribedByElementsArray, cachingInvariantMain.i.ariaDescribedByElements, "Caching invariant for ariaDescribedByElements");
assert_equals(ariaDetailsElementsArray, cachingInvariantMain.i.ariaDetailsElements, "Caching invariant for ariaDetailsElements");
assert_equals(ariaFlowToElementsArray, cachingInvariantMain.i.ariaFlowToElements, "Caching invariant for ariaFlowToElements");
assert_equals(ariaLabelledByElementsArray, cachingInvariantMain.i.ariaLabelledByElements, "Caching invariant for ariaLabelledByElements");
assert_equals(ariaOwnsElementsArray, cachingInvariantMain.i.ariaOwnsElements, "Caching invariant for ariaOwnsElements");
}, "Caching invariant different attributes.");
</script>
<custom-element id="cachingInvariantMain1"></custom-element>
<custom-element id="cachingInvariantMain2"></custom-element>
<script>
test(function(t) {
cachingInvariantMain1.i.ariaDescribedByElements = [cachingInvariantElement1, cachingInvariantElement2];
cachingInvariantMain2.i.ariaDescribedByElements = [cachingInvariantElement1, cachingInvariantElement2];
let ariaDescribedByElementsArray1 = cachingInvariantMain1.i.ariaDescribedByElements;
let ariaDescribedByElementsArray2 = cachingInvariantMain2.i.ariaDescribedByElements;
assert_equals(ariaDescribedByElementsArray1, cachingInvariantMain1.i.ariaDescribedByElements, "Caching invariant for ariaDescribedByElements in one elemnt");
assert_equals(ariaDescribedByElementsArray2, cachingInvariantMain2.i.ariaDescribedByElements, "Caching invariant for ariaDescribedByElements in onother elemnt");
assert_not_equals(cachingInvariantMain1.i.ariaDescribedByElements, cachingInvariantMain2.i.ariaDescribedByElements);
}, "Caching invariant different elements.");
</script>
</body>
</html>
|