@surea11y/core 1.1.0 → 1.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/CHANGELOG.md +25 -2
  2. package/docs/ENGINE_OPTIONS.md +2 -0
  3. package/docs/LIMITATIONS.md +1 -1
  4. package/package.json +1 -1
  5. package/src/checks/automatic/aria-allowed-attr.js +5 -4
  6. package/src/checks/automatic/aria-allowed-role.js +5 -4
  7. package/src/checks/automatic/aria-braille-equivalent.js +3 -4
  8. package/src/checks/automatic/aria-conditional-attr.js +3 -4
  9. package/src/checks/automatic/aria-deprecated-role.js +2 -3
  10. package/src/checks/automatic/aria-prohibited-attr.js +5 -4
  11. package/src/checks/automatic/aria-prohibited-children.js +2 -3
  12. package/src/checks/automatic/aria-required-attr.js +2 -3
  13. package/src/checks/automatic/aria-required-children.js +2 -3
  14. package/src/checks/automatic/aria-required-parent.js +3 -4
  15. package/src/checks/automatic/aria-roles-valid.js +2 -3
  16. package/src/checks/automatic/aria-valid-attr-value.js +5 -4
  17. package/src/checks/automatic/aria-valid-attr.js +5 -4
  18. package/src/checks/automatic/autocomplete-valid.js +2 -3
  19. package/src/checks/automatic/avoid-inline-spacing.js +2 -3
  20. package/src/checks/automatic/binary-control-name-present.js +2 -3
  21. package/src/checks/automatic/button-name-present.js +14 -4
  22. package/src/checks/automatic/bypass-blocks-present.js +35 -7
  23. package/src/checks/automatic/combobox-name-present.js +2 -3
  24. package/src/checks/automatic/definition-list-children-valid.js +2 -3
  25. package/src/checks/automatic/deprecated-elements-not-used.js +5 -4
  26. package/src/checks/automatic/dialog-name-present.js +2 -3
  27. package/src/checks/automatic/dlitem-parent-valid.js +2 -3
  28. package/src/checks/automatic/form-control-single-label.js +2 -3
  29. package/src/checks/automatic/iframe-focusable-content.js +2 -3
  30. package/src/checks/automatic/iframe-name-present.js +2 -3
  31. package/src/checks/automatic/iframe-title-unique.js +5 -5
  32. package/src/checks/automatic/label-in-name.js +7 -8
  33. package/src/checks/automatic/link-in-text-block.js +2 -3
  34. package/src/checks/automatic/link-name-present.js +16 -4
  35. package/src/checks/automatic/list-children-valid.js +2 -3
  36. package/src/checks/automatic/listbox-name-present.js +2 -3
  37. package/src/checks/automatic/listitem-parent-valid.js +2 -3
  38. package/src/checks/automatic/menuitem-name-present.js +2 -3
  39. package/src/checks/automatic/meter-name-present.js +2 -3
  40. package/src/checks/automatic/nested-interactive-controls-absent.js +2 -3
  41. package/src/checks/automatic/option-name-present.js +2 -3
  42. package/src/checks/automatic/progressbar-name-present.js +2 -3
  43. package/src/checks/automatic/searchbox-name-present.js +2 -3
  44. package/src/checks/automatic/server-side-image-map-absent.js +2 -3
  45. package/src/checks/automatic/slider-name-present.js +2 -3
  46. package/src/checks/automatic/spinbutton-name-present.js +2 -3
  47. package/src/checks/automatic/summary-name-present.js +2 -3
  48. package/src/checks/automatic/tab-name-present.js +2 -3
  49. package/src/checks/automatic/table-headers-attr-valid.js +5 -4
  50. package/src/checks/automatic/table-th-has-data-cells.js +5 -4
  51. package/src/checks/automatic/td-has-header.js +2 -3
  52. package/src/checks/automatic/textbox-name-present.js +2 -3
  53. package/src/checks/automatic/tooltip-name-present.js +2 -3
  54. package/src/checks/automatic/treeitem-name-present.js +2 -3
  55. package/src/checks/automatic/valid-lang.js +2 -3
  56. package/src/checks/manual/accesskeys-manual.js +1 -7
  57. package/src/checks/manual/aria-checked-state-mismatch-manual.js +7 -6
  58. package/src/checks/manual/aria-text-manual.js +2 -3
  59. package/src/checks/manual/focus-order-semantics-manual.js +2 -3
  60. package/src/checks/manual/identical-links-same-purpose-manual.js +2 -3
  61. package/src/checks/manual/image-redundant-alt-manual.js +2 -3
  62. package/src/checks/manual/label-title-only-manual.js +2 -3
  63. package/src/checks/manual/link-name-quality-manual.js +2 -3
  64. package/src/checks/manual/mouse-only-event-handlers-manual.js +2 -3
  65. package/src/checks/manual/no-autoplay-audio-manual.js +2 -3
  66. package/src/checks/manual/p-as-heading-manual.js +2 -3
  67. package/src/checks/manual/page-has-heading-one-manual.js +31 -2
  68. package/src/checks/manual/presentation-role-conflict-manual.js +3 -4
  69. package/src/checks/manual/scrollable-region-focusable-manual.js +2 -3
  70. package/src/checks/manual/skip-link-manual.js +91 -9
  71. package/src/checks/manual/table-duplicate-name-manual.js +2 -3
  72. package/src/checks/manual/table-fake-caption-manual.js +2 -3
  73. package/src/checks/manual/video-caption-manual.js +2 -3
  74. package/src/core/dom-helpers.js +89 -3
  75. package/src/core/dom-runner.js +19 -0
  76. package/src/core.js +962 -481
  77. package/src/i18n/en.js +4 -2
  78. package/src/i18n/fr.js +4 -2
@@ -17,10 +17,12 @@
17
17
  * rule has no partial-pass case, matching a widely-used reference engine's `blink`/`marquee`
18
18
  * rules (which report only when the element is found).
19
19
  * @implementation-notes
20
- * - Not gated on isAccTreeEligible: presence in markup is itself the
20
+ * - Not rule-gated on isAccTreeEligible: presence in markup is itself the
21
21
  * violation, independent of visibility (moving/blinking content inside a
22
22
  * hidden ancestor could still become visible later without a code
23
23
  * change, so hiding it today does not remove the underlying defect).
24
+ * Engine-level hidden-subtree filtering still applies unless
25
+ * engineOptions.includeHiddenElements is true.
24
26
  */
25
27
 
26
28
  const id = 'deprecated-elements-not-used';
@@ -46,10 +48,9 @@ const meta = {
46
48
  };
47
49
 
48
50
  function runInPage(ctx) {
49
- const { document, root, helpers, rule } = ctx;
50
- const safeRoot = root || document;
51
+ const { document, helpers, rule } = ctx;
51
52
 
52
- const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('blink, marquee', safeRoot) : helpers.queryAll('blink, marquee', safeRoot);
53
+ const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('blink, marquee') : helpers.queryAll('blink, marquee');
53
54
 
54
55
  const occurrences = [];
55
56
  let applicableCount = 0;
@@ -23,11 +23,10 @@ const meta = {
23
23
  };
24
24
 
25
25
  function runInPage(ctx) {
26
- const { document, root, helpers, rule } = ctx;
26
+ const { document, helpers, rule } = ctx;
27
27
  const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
28
28
  ? helpers.getEligibilityInfo
29
29
  : null;
30
- const safeRoot = root || document;
31
30
 
32
31
 
33
32
  function normalizeWs(s) {
@@ -141,7 +140,7 @@ function runInPage(ctx) {
141
140
  let applicableCount = 0;
142
141
 
143
142
  const selector = '[role="dialog"],[role="alertdialog"]';
144
- const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector, safeRoot) : helpers.queryAll(selector, safeRoot);
143
+ const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
145
144
 
146
145
 
147
146
  function evaluate(el) {
@@ -42,10 +42,9 @@ const meta = {
42
42
  };
43
43
 
44
44
  function runInPage(ctx) {
45
- const { document, root, helpers, rule } = ctx;
46
- const safeRoot = root || document;
45
+ const { document, helpers, rule } = ctx;
47
46
 
48
- const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('dt, dd', safeRoot) : helpers.queryAll('dt, dd', safeRoot);
47
+ const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('dt, dd') : helpers.queryAll('dt, dd');
49
48
 
50
49
  const occurrences = [];
51
50
  let applicableCount = 0;
@@ -46,11 +46,10 @@ const meta = {
46
46
  };
47
47
 
48
48
  function runInPage(ctx) {
49
- const { document, root, helpers, rule } = ctx;
50
- const safeRoot = root || document;
49
+ const { document, helpers, rule } = ctx;
51
50
 
52
51
  const selector = 'input:not([type="hidden"]):not([type="submit"]):not([type="reset"]):not([type="button"]):not([type="image"]),select,textarea';
53
- const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector, safeRoot) : helpers.queryAll(selector, safeRoot);
52
+ const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
54
53
 
55
54
  // Build a for-value -> label[] map once (avoids per-control dynamic
56
55
  // attribute-selector construction / CSS.escape, which is not guaranteed
@@ -55,8 +55,7 @@ const meta = {
55
55
  };
56
56
 
57
57
  function runInPage(ctx) {
58
- const { document, root, helpers, rule } = ctx;
59
- const safeRoot = root || document;
58
+ const { document, helpers, rule } = ctx;
60
59
 
61
60
  function hasFocusableCandidate(doc) {
62
61
  if (!doc || !doc.querySelectorAll) return false;
@@ -88,7 +87,7 @@ function runInPage(ctx) {
88
87
  return !Number.isNaN(n) && n < 0;
89
88
  }
90
89
 
91
- const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('iframe, frame', safeRoot) : helpers.queryAll('iframe, frame', safeRoot);
90
+ const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('iframe, frame') : helpers.queryAll('iframe, frame');
92
91
 
93
92
  const occurrences = [];
94
93
  let applicableCount = 0;
@@ -44,10 +44,9 @@ const meta = {
44
44
  };
45
45
 
46
46
  function runInPage(ctx) {
47
- const { document, root, helpers, rule } = ctx;
48
- const safeRoot = root || document;
47
+ const { document, helpers, rule } = ctx;
49
48
 
50
- const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('iframe, frame', safeRoot) : helpers.queryAll('iframe, frame', safeRoot);
49
+ const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('iframe, frame') : helpers.queryAll('iframe, frame');
51
50
 
52
51
  const occurrences = [];
53
52
  let applicableCount = 0;
@@ -19,8 +19,9 @@
19
19
  * - Compares the title ATTRIBUTE specifically, not the full computed
20
20
  * accessible name (aria-label could legitimately differ in wording even
21
21
  * when title happens to collide) — matches a widely-used reference engine's frame-title-unique.
22
- * - Not gated on isAccTreeEligible: a duplicate title is a static markup
23
- * property independent of the frame's current visibility.
22
+ * - Not rule-gated on isAccTreeEligible: duplicate titles are a static
23
+ * markup property. Engine-level hidden-subtree filtering still applies
24
+ * unless engineOptions.includeHiddenElements is true.
24
25
  */
25
26
 
26
27
  const id = 'iframe-title-unique';
@@ -46,10 +47,9 @@ const meta = {
46
47
  };
47
48
 
48
49
  function runInPage(ctx) {
49
- const { document, root, helpers, rule } = ctx;
50
- const safeRoot = root || document;
50
+ const { document, helpers, rule } = ctx;
51
51
 
52
- const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('iframe, frame', safeRoot) : helpers.queryAll('iframe, frame', safeRoot);
52
+ const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('iframe, frame') : helpers.queryAll('iframe, frame');
53
53
 
54
54
  const groups = new Map(); // trimmed title -> elements[]
55
55
  let applicableCount = 0;
@@ -30,8 +30,7 @@ const meta = {
30
30
  };
31
31
 
32
32
  function runInPage(ctx) {
33
- const { document, root, helpers, rule } = ctx;
34
- const safeRoot = root || document;
33
+ const { document, helpers, rule } = ctx;
35
34
 
36
35
  const occurrences = [];
37
36
  let applicableCount = 0;
@@ -40,7 +39,7 @@ function runInPage(ctx) {
40
39
  // NOTE: aria-hidden is intentionally NOT excluded here; it does not affect visual rendering.
41
40
  const selector = ':is(button, a[href], summary, input:not([type="hidden"]), textarea, select, [role="button"], [role="link"], [role="checkbox"], [role="radio"], [role="switch"], [role="searchbox"], [role="tab"], [role="menuitem"], [role="menuitemcheckbox"], [role="menuitemradio"], [role="option"], [role="treeitem"], [role="gridcell"]):not([hidden]):not([disabled]):not([aria-disabled="true"]):is([aria-label], [aria-labelledby])';
42
41
 
43
- const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector, safeRoot) : helpers.queryAll(selector, safeRoot);
42
+ const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
44
43
 
45
44
  function norm(s) {
46
45
  const v = (s == null ? '' : String(s));
@@ -178,11 +177,11 @@ function runInPage(ctx) {
178
177
  }
179
178
 
180
179
  // Fallback: label[for=id]. Uses `document` directly rather than
181
- // ctx.root/safeRoot -- label[for] association is a document-wide
182
- // relationship (IDs are document-unique), not bounded by whatever
183
- // contextSelector region happens to be scanned, and ctx.root is an
184
- // array (multi-region contextSelector support), not a single element
185
- // with its own .querySelector to call directly.
180
+ // ctx.root -- label[for] association is a document-wide relationship
181
+ // (IDs are document-unique), not bounded by whatever contextSelector
182
+ // region happens to be scanned, and ctx.root is an array (multi-region
183
+ // contextSelector support), not a single element with its own
184
+ // .querySelector to call directly.
186
185
  try {
187
186
  const idAttribute = control && control.getAttribute ? (control.getAttribute('id') || '') : '';
188
187
  const key = String(idAttribute || '').trim();
@@ -68,8 +68,7 @@ const meta = {
68
68
  };
69
69
 
70
70
  function runInPage(ctx) {
71
- const { document, root, helpers, rule, engineOptions } = ctx;
72
- const safeRoot = root || document;
71
+ const { document, helpers, rule, engineOptions } = ctx;
73
72
 
74
73
  function safeComputedStyle(el) {
75
74
  try {
@@ -115,7 +114,7 @@ function runInPage(ctx) {
115
114
  const c = helpers && helpers.contrast ? helpers.contrast : null;
116
115
 
117
116
  const selector = 'a[href]';
118
- const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector, safeRoot) : helpers.queryAll(selector, safeRoot);
117
+ const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
119
118
 
120
119
  const occurrences = [];
121
120
  let applicableCount = 0;
@@ -24,8 +24,7 @@ const meta = {
24
24
  };
25
25
 
26
26
  function runInPage(ctx) {
27
- const { document, root, helpers, rule } = ctx;
28
- const safeRoot = root || document;
27
+ const { document, helpers, rule } = ctx;
29
28
 
30
29
  const occurrences = [];
31
30
  let applicableCount = 0;
@@ -46,7 +45,7 @@ function runInPage(ctx) {
46
45
  }
47
46
 
48
47
  const selector = 'a[href], area[href], [role="link"]';
49
- const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector, safeRoot) : helpers.queryAll(selector, safeRoot);
48
+ const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
50
49
 
51
50
  for (const el of nodes) {
52
51
  // isAccTreeEligible returns { eligible, reasons }, not a boolean.
@@ -59,7 +58,20 @@ function runInPage(ctx) {
59
58
  const nameInfo = helpers.getAccessibleNameInfo ? helpers.getAccessibleNameInfo(el, ctx) : null;
60
59
  const programmaticName = (nameInfo && typeof nameInfo.value === 'string') ? nameInfo.value : '';
61
60
 
62
- const contentName = programmaticName.trim().length === 0 ? getConservativeSubtreeText(el) : '';
61
+ // A native <a href>/<area href> (or [role="link"]) whose role has been
62
+ // overridden to one of these roles is no longer semantically a link —
63
+ // per the WAI-ARIA Accessible Name and Description Computation spec
64
+ // these roles are name-from-author-only, and their rendered content
65
+ // represents a VALUE, not a NAME (mirrors axe-core's controlValueRoles,
66
+ // verified against its source; see button-name-present.js's identical
67
+ // fix for a real-world example — <button role="combobox">List</button> —
68
+ // of the same class of bug on a different host tag).
69
+ const role = el.getAttribute ? el.getAttribute('role') : null;
70
+ const roleNorm = String(role || '').replace(/\s+/g, ' ').trim().toLowerCase();
71
+ const VALUE_ROLES = ['textbox', 'progressbar', 'scrollbar', 'slider', 'spinbutton', 'combobox', 'listbox'];
72
+ const isContentNameCandidate = !VALUE_ROLES.includes(roleNorm);
73
+
74
+ const contentName = (programmaticName.trim().length === 0 && isContentNameCandidate) ? getConservativeSubtreeText(el) : '';
63
75
  const finalName = (programmaticName.trim().length ? programmaticName : contentName).trim();
64
76
 
65
77
  if (finalName.length === 0) {
@@ -71,14 +71,13 @@ const meta = {
71
71
  };
72
72
 
73
73
  function runInPage(ctx) {
74
- const { document, root, helpers, rule } = ctx;
75
- const safeRoot = root || document;
74
+ const { document, helpers, rule } = ctx;
76
75
 
77
76
  // Declared inside runInPage — see scripts/build-core.js header
78
77
  // ("runInPage MUST be self-contained").
79
78
  const ALLOWED_CHILD_TAGS = new Set(['li', 'script', 'template']);
80
79
 
81
- const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('ul, ol', safeRoot) : helpers.queryAll('ul, ol', safeRoot);
80
+ const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('ul, ol') : helpers.queryAll('ul, ol');
82
81
 
83
82
  const isAccTreeEligible = helpers && typeof helpers.isAccTreeEligible === 'function' ? helpers.isAccTreeEligible : null;
84
83
 
@@ -23,11 +23,10 @@ const meta = {
23
23
  };
24
24
 
25
25
  function runInPage(ctx) {
26
- const { document, root, helpers, rule } = ctx;
26
+ const { document, helpers, rule } = ctx;
27
27
  const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
28
28
  ? helpers.getEligibilityInfo
29
29
  : null;
30
- const safeRoot = root || document;
31
30
 
32
31
 
33
32
  function normalizeWs(s) {
@@ -132,7 +131,7 @@ function runInPage(ctx) {
132
131
  let applicableCount = 0;
133
132
 
134
133
  const selector = "[role=\"listbox\"]";
135
- const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector, safeRoot) : helpers.queryAll(selector, safeRoot);
134
+ const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
136
135
 
137
136
  // Precompute label[for] map for listbox elements that are labelable
138
137
  // native form controls (e.g. <select multiple role="listbox">).
@@ -56,10 +56,9 @@ const meta = {
56
56
  };
57
57
 
58
58
  function runInPage(ctx) {
59
- const { document, root, helpers, rule } = ctx;
60
- const safeRoot = root || document;
59
+ const { document, helpers, rule } = ctx;
61
60
 
62
- const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('li', safeRoot) : helpers.queryAll('li', safeRoot);
61
+ const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('li') : helpers.queryAll('li');
63
62
 
64
63
  const occurrences = [];
65
64
  let applicableCount = 0;
@@ -23,11 +23,10 @@ const meta = {
23
23
  };
24
24
 
25
25
  function runInPage(ctx) {
26
- const { document, root, helpers, rule } = ctx;
26
+ const { document, helpers, rule } = ctx;
27
27
  const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
28
28
  ? helpers.getEligibilityInfo
29
29
  : null;
30
- const safeRoot = root || document;
31
30
 
32
31
 
33
32
  function normalizeWs(s) {
@@ -133,7 +132,7 @@ function runInPage(ctx) {
133
132
  let applicableCount = 0;
134
133
 
135
134
  const selector = '[role="menuitem"],[role="menuitemcheckbox"],[role="menuitemradio"]';
136
- const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector, safeRoot) : helpers.queryAll(selector, safeRoot);
135
+ const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
137
136
 
138
137
 
139
138
  function evaluate(el) {
@@ -23,11 +23,10 @@ const meta = {
23
23
  };
24
24
 
25
25
  function runInPage(ctx) {
26
- const { document, root, helpers, rule } = ctx;
26
+ const { document, helpers, rule } = ctx;
27
27
  const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
28
28
  ? helpers.getEligibilityInfo
29
29
  : null;
30
- const safeRoot = root || document;
31
30
 
32
31
 
33
32
  function normalizeWs(s) {
@@ -91,7 +90,7 @@ function runInPage(ctx) {
91
90
  let applicableCount = 0;
92
91
 
93
92
  const selector = '[role="meter"]';
94
- const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector, safeRoot) : helpers.queryAll(selector, safeRoot);
93
+ const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
95
94
 
96
95
 
97
96
  function evaluate(el) {
@@ -53,8 +53,7 @@ const meta = {
53
53
  };
54
54
 
55
55
  function runInPage(ctx) {
56
- const { document, root, helpers, rule } = ctx;
57
- const safeRoot = root || document;
56
+ const { document, helpers, rule } = ctx;
58
57
 
59
58
  // Declared inside runInPage — see scripts/build-core.js header
60
59
  // ("runInPage MUST be self-contained").
@@ -83,7 +82,7 @@ function runInPage(ctx) {
83
82
  '[role="treeitem"]'
84
83
  ].join(', ');
85
84
 
86
- const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(INTERACTIVE_SELECTOR, safeRoot) : helpers.queryAll(INTERACTIVE_SELECTOR, safeRoot);
85
+ const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(INTERACTIVE_SELECTOR) : helpers.queryAll(INTERACTIVE_SELECTOR);
87
86
 
88
87
  const occurrences = [];
89
88
  let applicableCount = 0;
@@ -23,11 +23,10 @@ const meta = {
23
23
  };
24
24
 
25
25
  function runInPage(ctx) {
26
- const { document, root, helpers, rule } = ctx;
26
+ const { document, helpers, rule } = ctx;
27
27
  const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
28
28
  ? helpers.getEligibilityInfo
29
29
  : null;
30
- const safeRoot = root || document;
31
30
 
32
31
 
33
32
  function normalizeWs(s) {
@@ -91,7 +90,7 @@ function runInPage(ctx) {
91
90
  let applicableCount = 0;
92
91
 
93
92
  const selector = "[role=\"option\"]";
94
- const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector, safeRoot) : helpers.queryAll(selector, safeRoot);
93
+ const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
95
94
 
96
95
 
97
96
  function hasName(el) {
@@ -23,11 +23,10 @@ const meta = {
23
23
  };
24
24
 
25
25
  function runInPage(ctx) {
26
- const { document, root, helpers, rule } = ctx;
26
+ const { document, helpers, rule } = ctx;
27
27
  const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
28
28
  ? helpers.getEligibilityInfo
29
29
  : null;
30
- const safeRoot = root || document;
31
30
 
32
31
 
33
32
  function normalizeWs(s) {
@@ -91,7 +90,7 @@ function runInPage(ctx) {
91
90
  let applicableCount = 0;
92
91
 
93
92
  const selector = '[role="progressbar"]';
94
- const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector, safeRoot) : helpers.queryAll(selector, safeRoot);
93
+ const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
95
94
 
96
95
 
97
96
  function evaluate(el) {
@@ -23,11 +23,10 @@ const meta = {
23
23
  };
24
24
 
25
25
  function runInPage(ctx) {
26
- const { document, root, helpers, rule } = ctx;
26
+ const { document, helpers, rule } = ctx;
27
27
  const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
28
28
  ? helpers.getEligibilityInfo
29
29
  : null;
30
- const safeRoot = root || document;
31
30
 
32
31
 
33
32
  function normalizeWs(s) {
@@ -132,7 +131,7 @@ function runInPage(ctx) {
132
131
  let applicableCount = 0;
133
132
 
134
133
  const selector = "[role=\"searchbox\"]";
135
- const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector, safeRoot) : helpers.queryAll(selector, safeRoot);
134
+ const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
136
135
 
137
136
  // Precompute label[for] map for searchbox elements that are labelable
138
137
  // native form controls (e.g. <input role="searchbox">).
@@ -44,10 +44,9 @@ const meta = {
44
44
  };
45
45
 
46
46
  function runInPage(ctx) {
47
- const { document, root, helpers, rule } = ctx;
48
- const safeRoot = root || document;
47
+ const { document, helpers, rule } = ctx;
49
48
 
50
- const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('img[ismap]', safeRoot) : helpers.queryAll('img[ismap]', safeRoot);
49
+ const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('img[ismap]') : helpers.queryAll('img[ismap]');
51
50
 
52
51
  const occurrences = [];
53
52
  let applicableCount = 0;
@@ -23,11 +23,10 @@ const meta = {
23
23
  };
24
24
 
25
25
  function runInPage(ctx) {
26
- const { document, root, helpers, rule } = ctx;
26
+ const { document, helpers, rule } = ctx;
27
27
  const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
28
28
  ? helpers.getEligibilityInfo
29
29
  : null;
30
- const safeRoot = root || document;
31
30
 
32
31
 
33
32
  function normalizeWs(s) {
@@ -159,7 +158,7 @@ function runInPage(ctx) {
159
158
  let applicableCount = 0;
160
159
 
161
160
  const selector = 'input[type="range"], [role="slider"]';
162
- const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector, safeRoot) : helpers.queryAll(selector, safeRoot);
161
+ const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
163
162
 
164
163
 
165
164
  // Precompute label[for] map for native range inputs.
@@ -23,11 +23,10 @@ const meta = {
23
23
  };
24
24
 
25
25
  function runInPage(ctx) {
26
- const { document, root, helpers, rule } = ctx;
26
+ const { document, helpers, rule } = ctx;
27
27
  const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
28
28
  ? helpers.getEligibilityInfo
29
29
  : null;
30
- const safeRoot = root || document;
31
30
 
32
31
 
33
32
  function normalizeWs(s) {
@@ -132,7 +131,7 @@ function runInPage(ctx) {
132
131
  let applicableCount = 0;
133
132
 
134
133
  const selector = "[role=\"spinbutton\"]";
135
- const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector, safeRoot) : helpers.queryAll(selector, safeRoot);
134
+ const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
136
135
 
137
136
  // Precompute label[for] map for spinbutton elements that are labelable
138
137
  // native form controls (e.g. <input role="spinbutton">).
@@ -23,11 +23,10 @@ const meta = {
23
23
  };
24
24
 
25
25
  function runInPage(ctx) {
26
- const { document, root, helpers, rule } = ctx;
26
+ const { document, helpers, rule } = ctx;
27
27
  const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
28
28
  ? helpers.getEligibilityInfo
29
29
  : null;
30
- const safeRoot = root || document;
31
30
 
32
31
 
33
32
  function normalizeWs(s) {
@@ -90,7 +89,7 @@ function runInPage(ctx) {
90
89
  const occurrences = [];
91
90
  let applicableCount = 0;
92
91
 
93
- const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('summary', safeRoot) : helpers.queryAll('summary', safeRoot);
92
+ const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('summary') : helpers.queryAll('summary');
94
93
 
95
94
  function evaluate(el) {
96
95
  const ariaLabel = getAttr(el, 'aria-label');
@@ -23,11 +23,10 @@ const meta = {
23
23
  };
24
24
 
25
25
  function runInPage(ctx) {
26
- const { document, root, helpers, rule } = ctx;
26
+ const { document, helpers, rule } = ctx;
27
27
  const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
28
28
  ? helpers.getEligibilityInfo
29
29
  : null;
30
- const safeRoot = root || document;
31
30
 
32
31
 
33
32
  function normalizeWs(s) {
@@ -133,7 +132,7 @@ function runInPage(ctx) {
133
132
  let applicableCount = 0;
134
133
 
135
134
  const selector = '[role="tab"]';
136
- const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector, safeRoot) : helpers.queryAll(selector, safeRoot);
135
+ const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
137
136
 
138
137
 
139
138
  function evaluate(el) {
@@ -16,7 +16,9 @@
16
16
  * - One occurrence per offending cell (not per bad token), listing every
17
17
  * invalid reference — matches a widely-used reference engine's
18
18
  * td-headers-attr reporting granularity.
19
- * - Not gated on isAccTreeEligible: this is a static markup property.
19
+ * - Not rule-gated on isAccTreeEligible: this remains a static-markup
20
+ * property, while engine-level hidden-subtree filtering still applies
21
+ * unless engineOptions.includeHiddenElements is true.
20
22
  */
21
23
 
22
24
  const id = 'table-headers-attr-valid';
@@ -42,10 +44,9 @@ const meta = {
42
44
  };
43
45
 
44
46
  function runInPage(ctx) {
45
- const { document, root, helpers, rule } = ctx;
46
- const safeRoot = root || document;
47
+ const { document, helpers, rule } = ctx;
47
48
 
48
- const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('td[headers], th[headers]', safeRoot) : helpers.queryAll('td[headers], th[headers]', safeRoot);
49
+ const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('td[headers], th[headers]') : helpers.queryAll('td[headers], th[headers]');
49
50
 
50
51
  const occurrences = [];
51
52
  let applicableCount = 0;
@@ -28,7 +28,9 @@
28
28
  * if some particular <th> in it doesn't actually describe any cell
29
29
  * (false negative, not a false positive — acceptable under this
30
30
  * engine's philosophy).
31
- * - Not gated on isAccTreeEligible: this is a static markup property.
31
+ * - Not rule-gated on isAccTreeEligible: this remains a static-markup
32
+ * property, while engine-level hidden-subtree filtering still applies
33
+ * unless engineOptions.includeHiddenElements is true.
32
34
  */
33
35
 
34
36
  const id = 'table-th-has-data-cells';
@@ -54,10 +56,9 @@ const meta = {
54
56
  };
55
57
 
56
58
  function runInPage(ctx) {
57
- const { document, root, helpers, rule } = ctx;
58
- const safeRoot = root || document;
59
+ const { document, helpers, rule } = ctx;
59
60
 
60
- const tables = helpers.queryAllSmart ? helpers.queryAllSmart('table', safeRoot) : helpers.queryAll('table', safeRoot);
61
+ const tables = helpers.queryAllSmart ? helpers.queryAllSmart('table') : helpers.queryAll('table');
61
62
 
62
63
  const occurrences = [];
63
64
  let applicableCount = 0;
@@ -55,14 +55,13 @@ const meta = {
55
55
  };
56
56
 
57
57
  function runInPage(ctx) {
58
- const { document, root, helpers, rule } = ctx;
59
- const safeRoot = root || document;
58
+ const { document, helpers, rule } = ctx;
60
59
 
61
60
  const MIN_SIZE = 4;
62
61
 
63
62
  function trim(v) { return (v == null ? '' : String(v)).trim(); }
64
63
 
65
- const tables = helpers.queryAllSmart ? helpers.queryAllSmart('table', safeRoot) : helpers.queryAll('table', safeRoot);
64
+ const tables = helpers.queryAllSmart ? helpers.queryAllSmart('table') : helpers.queryAll('table');
66
65
 
67
66
  const occurrences = [];
68
67
  let applicableCount = 0;
@@ -23,11 +23,10 @@ const meta = {
23
23
  };
24
24
 
25
25
  function runInPage(ctx) {
26
- const { document, root, helpers, rule } = ctx;
26
+ const { document, helpers, rule } = ctx;
27
27
  const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
28
28
  ? helpers.getEligibilityInfo
29
29
  : null;
30
- const safeRoot = root || document;
31
30
 
32
31
 
33
32
  function normalizeWs(s) {
@@ -132,7 +131,7 @@ function runInPage(ctx) {
132
131
  let applicableCount = 0;
133
132
 
134
133
  const selector = "[role=\"textbox\"]";
135
- const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector, safeRoot) : helpers.queryAll(selector, safeRoot);
134
+ const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
136
135
 
137
136
  // Precompute label[for] map for textbox elements that are labelable
138
137
  // native form controls (e.g. <input role="textbox">).
@@ -23,11 +23,10 @@ const meta = {
23
23
  };
24
24
 
25
25
  function runInPage(ctx) {
26
- const { document, root, helpers, rule } = ctx;
26
+ const { document, helpers, rule } = ctx;
27
27
  const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
28
28
  ? helpers.getEligibilityInfo
29
29
  : null;
30
- const safeRoot = root || document;
31
30
 
32
31
 
33
32
  function normalizeWs(s) {
@@ -91,7 +90,7 @@ function runInPage(ctx) {
91
90
  let applicableCount = 0;
92
91
 
93
92
  const selector = '[role="tooltip"]';
94
- const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector, safeRoot) : helpers.queryAll(selector, safeRoot);
93
+ const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
95
94
 
96
95
 
97
96
  function evaluate(el) {