- commit
- 59c4ae58633197735b9549f49ffed1778f0ecbd9
- parent
- 50669758a836cd75e57ebb586d33c79f5fa7ec1b
- Author
- Tobias Bengfort <tobias.bengfort@posteo.de>
- Date
- 2024-02-04 14:29
fix: rework getRole - ignore invalid roles - ignore abstract roles - fall back to implicit role if all explicit roles are invalid
Diffstat
| M | lib/attrs.js | 38 | ++++++++++++++++++++------------------ |
| M | lib/constants.js | 60 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- |
| M | lib/name.js | 4 | +++- |
3 files changed, 82 insertions, 20 deletions
diff --git a/lib/attrs.js b/lib/attrs.js
@@ -8,40 +8,42 @@ var flatten = function(arr) {
8 8 return [].concat.apply([], arr);
9 9 };
10 10
11 -1 var normalizeRoles = function(roles) {
12 -1 return unique(roles.map(r => constants.aliases[r] || r));
-1 11 var normalizeRoles = function(roles, includeAbstract) {
-1 12 return unique(roles
-1 13 .map(r => constants.aliases[r] || r)
-1 14 .filter(r => constants.roles[r])
-1 15 .filter(r => includeAbstract || !constants.roles[r].abstract)
-1 16 );
13 17 };
14 18
15 19 // candidates can be passed for performance optimization
16 20 const getRole = function(el, candidates) {
17 -1 if (el.hasAttribute('role')) {
18 -1 let roles = el.getAttribute('role').toLowerCase().split(/\s+/);
19 -1 roles = normalizeRoles(roles);
20 -1 if (roles.length > 1 && candidates) {
21 -1 return [roles, candidates];
22 -1 }
-1 21 // TODO: filter out any invalid roles (e.g. name or context required)
-1 22 const roles = normalizeRoles(
-1 23 (el.getAttribute('role') || '').toLowerCase().split(/\s+/)
-1 24 );
-1 25
-1 26 if (roles.length > 1 && candidates) {
-1 27 return [roles, candidates];
-1 28 } else if (roles.length) {
23 29 for (const role of roles) {
24 30 if (!candidates || candidates.includes(role)) {
25 31 return role;
26 32 }
27 33 }
28 34 } else {
29 -1 const roles = candidates || Object.keys(constants.roles);
30 -1 for (const role of roles) {
-1 35 for (const role of (candidates || Object.keys(constants.roles))) {
31 36 const r = constants.roles[role];
32 -1 if (r) {
33 -1 const selector = (r.selectors || []).join(',');
34 -1 if (selector && el.matches(selector)) {
35 -1 return role;
36 -1 }
-1 37 if (!r.abstract && r.selectors && el.matches(r.selectors.join(','))) {
-1 38 return role;
37 39 }
38 40 }
39 41 }
40 42 };
41 43
42 44 const hasRole = function(el, roles) {
43 -1 const subRoles = normalizeRoles(roles).map(role => {
44 -1 return (constants.roles[role] || {}).subRoles || [role];
-1 45 const subRoles = normalizeRoles(roles, true).map(role => {
-1 46 return constants.roles[role].subRoles || [role];
45 47 });
46 48 return !!getRole(el, unique(flatten(subRoles)));
47 49 };
@@ -112,7 +114,7 @@ const getAttribute = function(el, key) {
112 114
113 115 if (key in constants.attrsWithDefaults) {
114 116 const role = getRole(el);
115 -1 const defaults = (constants.roles[role] || {}).defaults;
-1 117 const defaults = constants.roles[role].defaults;
116 118 if (defaults && defaults.hasOwnProperty(key)) {
117 119 return defaults[key];
118 120 }
diff --git a/lib/constants.js b/lib/constants.js
@@ -79,6 +79,8 @@ exports.roles = {
79 79 'atomic': true,
80 80 },
81 81 },
-1 82 alertdialog: {},
-1 83 application: {},
82 84 article: {
83 85 selectors: ['article'],
84 86 },
@@ -140,6 +142,7 @@ exports.roles = {
140 142 },
141 143 },
142 144 command: {
-1 145 abstract: true,
143 146 childRoles: ['button', 'link', 'menuitem'],
144 147 },
145 148 complementary: {
@@ -151,6 +154,7 @@ exports.roles = {
151 154 ],
152 155 },
153 156 composite: {
-1 157 abstract: true,
154 158 childRoles: ['grid', 'select', 'spinbutton', 'tablist'],
155 159 },
156 160 contentinfo: {
@@ -166,24 +170,60 @@ exports.roles = {
166 170 selectors: ['dialog'],
167 171 childRoles: ['alertdialog'],
168 172 },
-1 173 directory: {},
-1 174 'doc-abstract': {},
-1 175 'doc-acknowledgments': {},
-1 176 'doc-afterword': {},
-1 177 'doc-appendix': {},
169 178 'doc-backlink': {
170 179 nameFromContents: true,
171 180 },
-1 181 'doc-biblioentry': {},
-1 182 'doc-bibliography': {},
172 183 'doc-biblioref': {
173 184 nameFromContents: true,
174 185 },
-1 186 'doc-chapter': {},
-1 187 'doc-colophon': {},
-1 188 'doc-conclusion': {},
-1 189 'doc-cover': {},
-1 190 'doc-credit': {},
-1 191 'doc-credits': {},
-1 192 'doc-dedication': {},
-1 193 'doc-endnote': {},
-1 194 'doc-endnotes': {},
-1 195 'doc-epilogue': {},
-1 196 'doc-epigraph': {},
-1 197 'doc-errata': {},
-1 198 'doc-example': {},
-1 199 'doc-footnote': {},
-1 200 'doc-foreword': {},
-1 201 'doc-glossary': {},
175 202 'doc-glossref': {
176 203 nameFromContents: true,
177 204 },
-1 205 'doc-index': {},
-1 206 'doc-introduction': {},
178 207 'doc-noteref': {
179 208 nameFromContents: true,
180 209 },
-1 210 'doc-notice': {},
181 211 'doc-pagebreak': {
182 212 nameFromContents: true,
183 213 },
-1 214 'doc-pagefooter': {},
-1 215 'doc-pageheader': {},
-1 216 'doc-pagelist': {},
-1 217 'doc-part': {},
-1 218 'doc-preface': {},
-1 219 'doc-prologue': {},
-1 220 'doc-pullquote': {},
-1 221 'doc-qna': {},
184 222 'doc-subtitle': {
185 223 nameFromContents: true,
186 224 },
-1 225 'doc-tip': {},
-1 226 'doc-toc': {},
187 227 document: {
188 228 selectors: ['html'],
189 229 childRoles: ['article', 'graphics-document'],
@@ -191,6 +231,7 @@ exports.roles = {
191 231 emphasis: {
192 232 selectors: ['em'],
193 233 },
-1 234 feed: {},
194 235 figure: {
195 236 selectors: ['figure'],
196 237 childRoles: ['doc-example'],
@@ -225,6 +266,8 @@ exports.roles = {
225 266 'graphics-document': {
226 267 selectors: ['svg'],
227 268 },
-1 269 'graphics-object': {},
-1 270 'graphics-symbol': {},
228 271 grid: {
229 272 childRoles: ['treegrid'],
230 273 },
@@ -255,6 +298,7 @@ exports.roles = {
255 298 childRoles: ['doc-cover'],
256 299 },
257 300 input: {
-1 301 abstract: true,
258 302 childRoles: [
259 303 'checkbox',
260 304 'combobox',
@@ -269,6 +313,7 @@ exports.roles = {
269 313 selectors: ['ins'],
270 314 },
271 315 landmark: {
-1 316 abstract: true,
272 317 childRoles: [
273 318 'banner',
274 319 'complementary',
@@ -327,6 +372,7 @@ exports.roles = {
327 372 main: {
328 373 selectors: ['main'],
329 374 },
-1 375 marquee: {},
330 376 math: {
331 377 selectors: ['math'],
332 378 },
@@ -401,13 +447,16 @@ exports.roles = {
401 447 'checked': 'false',
402 448 },
403 449 },
-1 450 radiogroup: {},
404 451 range: {
-1 452 abstract: true,
405 453 childRoles: ['meter', 'progressbar', 'scrollbar', 'slider', 'spinbutton'],
406 454 },
407 455 region: {
408 456 selectors: ['section[aria-label]', 'section[aria-labelledby]', 'section[title]'],
409 457 },
410 458 roletype: {
-1 459 abstract: true,
411 460 childRoles: ['structure', 'widget', 'window'],
412 461 },
413 462 row: {
@@ -435,6 +484,7 @@ exports.roles = {
435 484 selectors: ['input[type="search"]:not([list])'],
436 485 },
437 486 section: {
-1 487 abstract: true,
438 488 childRoles: [
439 489 'alert',
440 490 'blockquote',
@@ -478,6 +528,7 @@ exports.roles = {
478 528 ],
479 529 },
480 530 sectionhead: {
-1 531 abstract: true,
481 532 childRoles: [
482 533 'columnheader',
483 534 'doc-subtitle',
@@ -488,6 +539,7 @@ exports.roles = {
488 539 nameFromContents: true,
489 540 },
490 541 select: {
-1 542 abstract: true,
491 543 childRoles: ['listbox', 'menu', 'radiogroup', 'tree'],
492 544 },
493 545 separator: {
@@ -528,6 +580,7 @@ exports.roles = {
528 580 selectors: ['strong'],
529 581 },
530 582 structure: {
-1 583 abstract: true,
531 584 childRoles: [
532 585 'application',
533 586 'document',
@@ -540,6 +593,7 @@ exports.roles = {
540 593 'separator',
541 594 ],
542 595 },
-1 596 suggestion: {},
543 597 subscript: {
544 598 selectors: ['sub'],
545 599 },
@@ -567,6 +621,7 @@ exports.roles = {
567 621 'orientation': 'horizontal',
568 622 },
569 623 },
-1 624 tabpanel: {},
570 625 term: {
571 626 selectors: ['dfn', 'dt'],
572 627 },
@@ -603,10 +658,12 @@ exports.roles = {
603 658 'orientation': 'vertical',
604 659 },
605 660 },
-1 661 treegrid: {},
606 662 treeitem: {
607 663 nameFromContents: true,
608 664 },
609 665 widget: {
-1 666 abstract: true,
610 667 childRoles: [
611 668 'command',
612 669 'composite',
@@ -620,12 +677,13 @@ exports.roles = {
620 677 ],
621 678 },
622 679 window: {
-1 680 abstract: true,
623 681 childRoles: ['dialog'],
624 682 },
625 683 };
626 684
627 685 const getSubRoles = function(role) {
628 -1 const children = (exports.roles[role] || {}).childRoles || [];
-1 686 const children = (exports.roles[role]).childRoles || [];
629 687 const descendents = children.map(getSubRoles);
630 688
631 689 const result = [role];
diff --git a/lib/name.js b/lib/name.js
@@ -46,7 +46,9 @@ const getContent = function(root, visited) {
46 46
47 47 const allowNameFromContent = function(el) {
48 48 const role = query.getRole(el);
49 -1 return (constants.roles[role] || {}).nameFromContents;
-1 49 if (role) {
-1 50 return constants.roles[role].nameFromContents;
-1 51 }
50 52 };
51 53
52 54 const getName = function(el, recursive, visited, directReference) {