@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.
- package/CHANGELOG.md +25 -2
- package/docs/ENGINE_OPTIONS.md +2 -0
- package/docs/LIMITATIONS.md +1 -1
- package/package.json +1 -1
- package/src/checks/automatic/aria-allowed-attr.js +5 -4
- package/src/checks/automatic/aria-allowed-role.js +5 -4
- package/src/checks/automatic/aria-braille-equivalent.js +3 -4
- package/src/checks/automatic/aria-conditional-attr.js +3 -4
- package/src/checks/automatic/aria-deprecated-role.js +2 -3
- package/src/checks/automatic/aria-prohibited-attr.js +5 -4
- package/src/checks/automatic/aria-prohibited-children.js +2 -3
- package/src/checks/automatic/aria-required-attr.js +2 -3
- package/src/checks/automatic/aria-required-children.js +2 -3
- package/src/checks/automatic/aria-required-parent.js +3 -4
- package/src/checks/automatic/aria-roles-valid.js +2 -3
- package/src/checks/automatic/aria-valid-attr-value.js +5 -4
- package/src/checks/automatic/aria-valid-attr.js +5 -4
- package/src/checks/automatic/autocomplete-valid.js +2 -3
- package/src/checks/automatic/avoid-inline-spacing.js +2 -3
- package/src/checks/automatic/binary-control-name-present.js +2 -3
- package/src/checks/automatic/button-name-present.js +14 -4
- package/src/checks/automatic/bypass-blocks-present.js +35 -7
- package/src/checks/automatic/combobox-name-present.js +2 -3
- package/src/checks/automatic/definition-list-children-valid.js +2 -3
- package/src/checks/automatic/deprecated-elements-not-used.js +5 -4
- package/src/checks/automatic/dialog-name-present.js +2 -3
- package/src/checks/automatic/dlitem-parent-valid.js +2 -3
- package/src/checks/automatic/form-control-single-label.js +2 -3
- package/src/checks/automatic/iframe-focusable-content.js +2 -3
- package/src/checks/automatic/iframe-name-present.js +2 -3
- package/src/checks/automatic/iframe-title-unique.js +5 -5
- package/src/checks/automatic/label-in-name.js +7 -8
- package/src/checks/automatic/link-in-text-block.js +2 -3
- package/src/checks/automatic/link-name-present.js +16 -4
- package/src/checks/automatic/list-children-valid.js +2 -3
- package/src/checks/automatic/listbox-name-present.js +2 -3
- package/src/checks/automatic/listitem-parent-valid.js +2 -3
- package/src/checks/automatic/menuitem-name-present.js +2 -3
- package/src/checks/automatic/meter-name-present.js +2 -3
- package/src/checks/automatic/nested-interactive-controls-absent.js +2 -3
- package/src/checks/automatic/option-name-present.js +2 -3
- package/src/checks/automatic/progressbar-name-present.js +2 -3
- package/src/checks/automatic/searchbox-name-present.js +2 -3
- package/src/checks/automatic/server-side-image-map-absent.js +2 -3
- package/src/checks/automatic/slider-name-present.js +2 -3
- package/src/checks/automatic/spinbutton-name-present.js +2 -3
- package/src/checks/automatic/summary-name-present.js +2 -3
- package/src/checks/automatic/tab-name-present.js +2 -3
- package/src/checks/automatic/table-headers-attr-valid.js +5 -4
- package/src/checks/automatic/table-th-has-data-cells.js +5 -4
- package/src/checks/automatic/td-has-header.js +2 -3
- package/src/checks/automatic/textbox-name-present.js +2 -3
- package/src/checks/automatic/tooltip-name-present.js +2 -3
- package/src/checks/automatic/treeitem-name-present.js +2 -3
- package/src/checks/automatic/valid-lang.js +2 -3
- package/src/checks/manual/accesskeys-manual.js +1 -7
- package/src/checks/manual/aria-checked-state-mismatch-manual.js +7 -6
- package/src/checks/manual/aria-text-manual.js +2 -3
- package/src/checks/manual/focus-order-semantics-manual.js +2 -3
- package/src/checks/manual/identical-links-same-purpose-manual.js +2 -3
- package/src/checks/manual/image-redundant-alt-manual.js +2 -3
- package/src/checks/manual/label-title-only-manual.js +2 -3
- package/src/checks/manual/link-name-quality-manual.js +2 -3
- package/src/checks/manual/mouse-only-event-handlers-manual.js +2 -3
- package/src/checks/manual/no-autoplay-audio-manual.js +2 -3
- package/src/checks/manual/p-as-heading-manual.js +2 -3
- package/src/checks/manual/page-has-heading-one-manual.js +31 -2
- package/src/checks/manual/presentation-role-conflict-manual.js +3 -4
- package/src/checks/manual/scrollable-region-focusable-manual.js +2 -3
- package/src/checks/manual/skip-link-manual.js +91 -9
- package/src/checks/manual/table-duplicate-name-manual.js +2 -3
- package/src/checks/manual/table-fake-caption-manual.js +2 -3
- package/src/checks/manual/video-caption-manual.js +2 -3
- package/src/core/dom-helpers.js +89 -3
- package/src/core/dom-runner.js +19 -0
- package/src/core.js +962 -481
- package/src/i18n/en.js +4 -2
- package/src/i18n/fr.js +4 -2
package/src/core.js
CHANGED
|
@@ -5316,8 +5316,8 @@ const CHECK_DEFS = [
|
|
|
5316
5316
|
},
|
|
5317
5317
|
{
|
|
5318
5318
|
"ruleId": "skip-link",
|
|
5319
|
-
"title": "Skip link must have a resolvable target",
|
|
5320
|
-
"description": "Checks that a \"skip to ...\" link's href fragment resolves to a real element in the document.",
|
|
5319
|
+
"title": "Skip link must have a resolvable, usable target",
|
|
5320
|
+
"description": "Checks that a \"skip to ...\" link's href fragment resolves to a real, currently usable element in the document.",
|
|
5321
5321
|
"i18n": {
|
|
5322
5322
|
"titleKey": "skipLink_title",
|
|
5323
5323
|
"descriptionKey": "skipLink_description"
|
|
@@ -7556,10 +7556,12 @@ const I18N = {
|
|
|
7556
7556
|
"region_description": "Checks that direct children of <body> with visible text content are contained within a landmark region.",
|
|
7557
7557
|
"region_summary_cantTell": "This content is not contained within a landmark region.",
|
|
7558
7558
|
"region_hint_cantTell": "Move this content inside a landmark region (main, nav, aside, a labeled section, etc.).",
|
|
7559
|
-
"skipLink_title": "Skip link must have a resolvable target",
|
|
7560
|
-
"skipLink_description": "Checks that a \"skip to ...\" link's href fragment resolves to a real element in the document.",
|
|
7559
|
+
"skipLink_title": "Skip link must have a resolvable, usable target",
|
|
7560
|
+
"skipLink_description": "Checks that a \"skip to ...\" link's href fragment resolves to a real, currently usable element in the document.",
|
|
7561
7561
|
"skipLink_summary_cantTell": "This skip link's target does not exist.",
|
|
7562
7562
|
"skipLink_hint_cantTell": "Point the skip link's href at an id that exists in the document, or add the missing target element.",
|
|
7563
|
+
"skipLink_summary_unusableTarget_cantTell": "This skip link points to a target that exists but is not currently usable.",
|
|
7564
|
+
"skipLink_hint_unusableTarget_cantTell": "Point this skip link to a target that is exposed and usable as a navigation destination.",
|
|
7563
7565
|
"autocompleteValid_title": "autocomplete attribute must be a valid autofill value",
|
|
7564
7566
|
"autocompleteValid_description": "Checks that a non-empty autocomplete attribute is \"on\"/\"off\" or a well-formed autofill detail token list.",
|
|
7565
7567
|
"autocompleteValid_summary_fail": "This autocomplete attribute value is not a valid autofill value.",
|
|
@@ -8158,10 +8160,12 @@ const I18N = {
|
|
|
8158
8160
|
"region_description": "Vérifie que les enfants directs de <body> ayant un contenu textuel visible sont contenus dans un point de repère.",
|
|
8159
8161
|
"region_summary_cantTell": "Ce contenu n’est contenu dans aucun point de repère.",
|
|
8160
8162
|
"region_hint_cantTell": "Déplacez ce contenu à l’intérieur d’un point de repère (main, nav, aside, une section nommée, etc.).",
|
|
8161
|
-
"skipLink_title": "Un lien d’évitement doit avoir une cible qui se résout",
|
|
8162
|
-
"skipLink_description": "Vérifie que le fragment href d’un lien « aller au... » se résout vers un élément réel du document.",
|
|
8163
|
+
"skipLink_title": "Un lien d’évitement doit avoir une cible qui se résout et qui est utilisable",
|
|
8164
|
+
"skipLink_description": "Vérifie que le fragment href d’un lien « aller au... » se résout vers un élément réel et actuellement utilisable du document.",
|
|
8163
8165
|
"skipLink_summary_cantTell": "La cible de ce lien d’évitement n’existe pas.",
|
|
8164
8166
|
"skipLink_hint_cantTell": "Faites pointer le href du lien d’évitement vers un id qui existe dans le document, ou ajoutez l’élément cible manquant.",
|
|
8167
|
+
"skipLink_summary_unusableTarget_cantTell": "Ce lien d’évitement pointe vers une cible qui existe mais qui n’est pas actuellement utilisable.",
|
|
8168
|
+
"skipLink_hint_unusableTarget_cantTell": "Faites pointer ce lien d’évitement vers une cible exposée et utilisable comme destination de navigation.",
|
|
8165
8169
|
"autocompleteValid_title": "L’attribut autocomplete doit être une valeur d’auto-remplissage valide",
|
|
8166
8170
|
"autocompleteValid_description": "Vérifie qu’un attribut autocomplete non vide vaut « on »/« off » ou une liste de jetons d’auto-remplissage bien formée.",
|
|
8167
8171
|
"autocompleteValid_summary_fail": "Cette valeur d’attribut autocomplete n’est pas une valeur d’auto-remplissage valide.",
|
|
@@ -10967,6 +10971,10 @@ const createDomHelpers = (function createDomHelpers(opts) {
|
|
|
10967
10971
|
})();
|
|
10968
10972
|
// Default on: opt OUT with `includeShadowDom: false`, not opt in.
|
|
10969
10973
|
const includeShadowDom = !(opts && opts.includeShadowDom === false);
|
|
10974
|
+
// Default off: by default, helper queries skip structurally/CSS-hidden
|
|
10975
|
+
// subtrees (display:none, [hidden], closed <details>, etc.). Callers can
|
|
10976
|
+
// opt out with includeHiddenElements:true.
|
|
10977
|
+
const includeHiddenElements = !!(opts && opts.includeHiddenElements === true);
|
|
10970
10978
|
const excludeSelectors = Array.isArray(opts && opts.excludeSelectors) ? opts.excludeSelectors : [];
|
|
10971
10979
|
|
|
10972
10980
|
// Rule-scoped excludes (engineOptions.rules[ruleId].excludeSelectors), set
|
|
@@ -11933,8 +11941,55 @@ const createDomHelpers = (function createDomHelpers(opts) {
|
|
|
11933
11941
|
return results;
|
|
11934
11942
|
}
|
|
11935
11943
|
|
|
11944
|
+
const HARD_HIDDEN_REASONS = new Set([
|
|
11945
|
+
'displayNone',
|
|
11946
|
+
'hiddenAttr',
|
|
11947
|
+
'detailsClosed',
|
|
11948
|
+
'templateContent',
|
|
11949
|
+
'nonRenderedElement',
|
|
11950
|
+
'inputHidden',
|
|
11951
|
+
'visibilityHidden',
|
|
11952
|
+
'contentVisibilityHidden'
|
|
11953
|
+
]);
|
|
11954
|
+
|
|
11936
11955
|
function queryAllSmart(sel) {
|
|
11937
|
-
|
|
11956
|
+
let list = includeShadowDom ? queryAllDeep(sel) : queryAll(sel);
|
|
11957
|
+
|
|
11958
|
+
// Global hidden-content policy: skip nodes that are fully excluded from
|
|
11959
|
+
// rendered visibility by default (unless includeHiddenElements:true).
|
|
11960
|
+
if (!includeHiddenElements) {
|
|
11961
|
+
list = list.filter((el) => {
|
|
11962
|
+
try {
|
|
11963
|
+
const vis = isAccTreeEligible(el);
|
|
11964
|
+
if (!vis || vis.eligible !== false) return true;
|
|
11965
|
+
const reasons = Array.isArray(vis.reasons) ? vis.reasons : [];
|
|
11966
|
+
for (const r of reasons) {
|
|
11967
|
+
if (HARD_HIDDEN_REASONS.has(r)) return false;
|
|
11968
|
+
}
|
|
11969
|
+
|
|
11970
|
+
// `isAccTreeEligible` can short-circuit on an inert ancestor
|
|
11971
|
+
// before it reaches an outer hard-hidden ancestor (e.g.
|
|
11972
|
+
// display:none wrapper). In that case the node is still
|
|
11973
|
+
// structurally hidden and should be excluded by the default
|
|
11974
|
+
// hidden-content policy.
|
|
11975
|
+
if (reasons.includes('inert')) {
|
|
11976
|
+
const domVis = isDomVisibleEligible(el, null, {
|
|
11977
|
+
visibilityMode: 'styleOnly',
|
|
11978
|
+
disableGeometry: true,
|
|
11979
|
+
ignoreOpacity: true
|
|
11980
|
+
});
|
|
11981
|
+
const domReasons = Array.isArray(domVis && domVis.reasons) ? domVis.reasons : [];
|
|
11982
|
+
for (const r of domReasons) {
|
|
11983
|
+
if (HARD_HIDDEN_REASONS.has(r)) return false;
|
|
11984
|
+
}
|
|
11985
|
+
}
|
|
11986
|
+
return true;
|
|
11987
|
+
} catch {
|
|
11988
|
+
return true;
|
|
11989
|
+
}
|
|
11990
|
+
});
|
|
11991
|
+
}
|
|
11992
|
+
|
|
11938
11993
|
return __getEffectiveExcludeSelectors().length ? list.filter((el) => !isExcluded(el)) : list;
|
|
11939
11994
|
}
|
|
11940
11995
|
|
|
@@ -14504,6 +14559,36 @@ const createDomHelpers = (function createDomHelpers(opts) {
|
|
|
14504
14559
|
let node = el;
|
|
14505
14560
|
let safety = 0;
|
|
14506
14561
|
|
|
14562
|
+
// Only apply the "stop climbing once we reach a contextSelector-
|
|
14563
|
+
// matched root" shortcut when there's a single (or no) matched
|
|
14564
|
+
// root -- resolveContextRoots() falls back to `[documentElement]`
|
|
14565
|
+
// when no contextSelector is given, so this is the overwhelmingly
|
|
14566
|
+
// common case and behaves exactly as before.
|
|
14567
|
+
//
|
|
14568
|
+
// With MULTIPLE matched roots (multi-region contextSelector
|
|
14569
|
+
// scans), stopping there without recording anything about which
|
|
14570
|
+
// root produced an ambiguous, non-unique selector string for two
|
|
14571
|
+
// structurally-identical regions -- a real, confirmed bug (found
|
|
14572
|
+
// 2026-07-29 via the cross-engine comparisons project): two
|
|
14573
|
+
// wrapper <div>s, each containing two identical ".widget"
|
|
14574
|
+
// sections scanned via `contextSelector: '.widget'`, produced the
|
|
14575
|
+
// *same* selector string ("section:nth-of-type(1) > div > div >
|
|
14576
|
+
// button") for the equivalent button in each wrapper --
|
|
14577
|
+
// resolving to 2 elements instead of 1 when queried, and pointing
|
|
14578
|
+
// at the wrong one for at least one of the two occurrences. The
|
|
14579
|
+
// existing `el.matches(candidate)` safety check below couldn't
|
|
14580
|
+
// catch this: it only verifies THIS element matches the string,
|
|
14581
|
+
// never that the string is unique document-wide.
|
|
14582
|
+
//
|
|
14583
|
+
// Fix: when multiple roots are in play, don't stop early --
|
|
14584
|
+
// keep climbing (same as the always-correct no-contextSelector
|
|
14585
|
+
// path) until finding a genuinely unique anchor or reaching the
|
|
14586
|
+
// true document root, which is always singular. That restores
|
|
14587
|
+
// the invariant the final safety-check comment below relies on,
|
|
14588
|
+
// rather than needing a separate (more expensive) document-wide
|
|
14589
|
+
// uniqueness re-check.
|
|
14590
|
+
const stopAtMatchedRoot = roots.length <= 1;
|
|
14591
|
+
|
|
14507
14592
|
while (node && node.nodeType === 1 && safety++ < 20) {
|
|
14508
14593
|
let anchor = null;
|
|
14509
14594
|
|
|
@@ -14543,7 +14628,7 @@ const createDomHelpers = (function createDomHelpers(opts) {
|
|
|
14543
14628
|
parts.unshift(nthOfType(node));
|
|
14544
14629
|
}
|
|
14545
14630
|
|
|
14546
|
-
if (!node.parentElement || roots.includes(node)) break;
|
|
14631
|
+
if (!node.parentElement || (stopAtMatchedRoot && roots.includes(node))) break;
|
|
14547
14632
|
node = node.parentElement;
|
|
14548
14633
|
}
|
|
14549
14634
|
|
|
@@ -14562,7 +14647,12 @@ const createDomHelpers = (function createDomHelpers(opts) {
|
|
|
14562
14647
|
// position relative to its own parent via `>` (child, not
|
|
14563
14648
|
// descendant) combinators, so a correctly-matching chain can
|
|
14564
14649
|
// only resolve to one element short of a malformed document
|
|
14565
|
-
// (e.g. two <html> roots)
|
|
14650
|
+
// (e.g. two <html> roots) -- true as long as the walk above
|
|
14651
|
+
// never stops short of a genuinely unique anchor/root, which is
|
|
14652
|
+
// exactly what `stopAtMatchedRoot` now guarantees (see its own
|
|
14653
|
+
// comment above; a multi-root contextSelector scan stopping
|
|
14654
|
+
// early used to violate this invariant silently). Re-deriving
|
|
14655
|
+
// that guarantee via a
|
|
14566
14656
|
// document-wide :nth-of-type scan was measured to cost O(total
|
|
14567
14657
|
// same-tag siblings) per call — pathological on pages with many
|
|
14568
14658
|
// flat, unidentified siblings (e.g. hundreds of unlabeled
|
|
@@ -15129,6 +15219,9 @@ const runCore = (function runCore(pageUrl, contextSelector, engineOptions, runOn
|
|
|
15129
15219
|
|
|
15130
15220
|
// Default on: opt OUT with `includeShadowDom: false`, not opt in.
|
|
15131
15221
|
const includeShadowDom = !(engineOptionsResolved && engineOptionsResolved.includeShadowDom === false);
|
|
15222
|
+
// Default off: hidden/collapsed content is excluded from rule evaluation
|
|
15223
|
+
// unless the caller explicitly opts in.
|
|
15224
|
+
const includeHiddenElements = !!(engineOptionsResolved && engineOptionsResolved.includeHiddenElements === true);
|
|
15132
15225
|
const excludeSelectors = normalizeSelectorList(engineOptionsResolved && engineOptionsResolved.excludeSelectors);
|
|
15133
15226
|
|
|
15134
15227
|
const url = pageUrl || (document.location && document.location.href) || null;
|
|
@@ -15139,11 +15232,27 @@ const runCore = (function runCore(pageUrl, contextSelector, engineOptions, runOn
|
|
|
15139
15232
|
? engineOptionsResolved.timestamp.trim()
|
|
15140
15233
|
: null;
|
|
15141
15234
|
|
|
15235
|
+
// createDomHelpers()/createContrastHelpers() persist their element-keyed
|
|
15236
|
+
// caches (outerHtmlCache, selectorCache, etc.) on window.__a11ycoreSharedCache
|
|
15237
|
+
// so multiple helper instances created *within this run* can share them
|
|
15238
|
+
// deterministically. But a window/document is frequently reused across
|
|
15239
|
+
// SEPARATE runs -- e.g. Jest's jsdom environment creates one window per
|
|
15240
|
+
// test file, and mutating document.body between it() blocks is standard.
|
|
15241
|
+
// Those caches are keyed by element reference, not content, so a run that
|
|
15242
|
+
// reuses an already-cached element (document.body never changes identity)
|
|
15243
|
+
// would otherwise read stale data cached by an earlier, unrelated run on
|
|
15244
|
+
// the same window. Clearing at the start of every run keeps sharing scoped
|
|
15245
|
+
// to "this run" as intended, without leaking across runs.
|
|
15246
|
+
try {
|
|
15247
|
+
if (window && window.__a11ycoreSharedCache) window.__a11ycoreSharedCache = {};
|
|
15248
|
+
} catch {}
|
|
15249
|
+
|
|
15142
15250
|
const sharedHelpers = createDomHelpers({
|
|
15143
15251
|
document,
|
|
15144
15252
|
window,
|
|
15145
15253
|
root: roots,
|
|
15146
15254
|
includeShadowDom,
|
|
15255
|
+
includeHiddenElements,
|
|
15147
15256
|
excludeSelectors,
|
|
15148
15257
|
// Optional perf counters (bench/debug only). Deterministic and per-run.
|
|
15149
15258
|
perfStats: !!(engineOptionsResolved && engineOptionsResolved.perfStats)
|
|
@@ -21247,8 +21356,8 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
21247
21356
|
},
|
|
21248
21357
|
{
|
|
21249
21358
|
"ruleId": "skip-link",
|
|
21250
|
-
"title": "Skip link must have a resolvable target",
|
|
21251
|
-
"description": "Checks that a \"skip to ...\" link's href fragment resolves to a real element in the document.",
|
|
21359
|
+
"title": "Skip link must have a resolvable, usable target",
|
|
21360
|
+
"description": "Checks that a \"skip to ...\" link's href fragment resolves to a real, currently usable element in the document.",
|
|
21252
21361
|
"i18n": {
|
|
21253
21362
|
"titleKey": "skipLink_title",
|
|
21254
21363
|
"descriptionKey": "skipLink_description"
|
|
@@ -22840,13 +22949,7 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
22840
22949
|
const RULE_IMPLS = {
|
|
22841
22950
|
"accesskeys": { run: (function runInPage(ctx) {
|
|
22842
22951
|
const { document, helpers, rule } = ctx;
|
|
22843
|
-
|
|
22844
|
-
let nodes = [];
|
|
22845
|
-
try {
|
|
22846
|
-
nodes = document.querySelectorAll('[accesskey]');
|
|
22847
|
-
} catch {
|
|
22848
|
-
nodes = [];
|
|
22849
|
-
}
|
|
22952
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[accesskey]') : helpers.queryAll('[accesskey]');
|
|
22850
22953
|
|
|
22851
22954
|
const groups = new Map(); // normalized key -> elements[]
|
|
22852
22955
|
for (const el of nodes) {
|
|
@@ -23437,8 +23540,7 @@ if (isAccTreeEligible) {
|
|
|
23437
23540
|
return { ruleId: rule.ruleId, outcome: 'cantTell', severity: 'minor', occurrences };
|
|
23438
23541
|
}), applicability: null },
|
|
23439
23542
|
"aria-allowed-attr": { run: (function runInPage(ctx) {
|
|
23440
|
-
const { document,
|
|
23441
|
-
const safeRoot = root || document;
|
|
23543
|
+
const { document, helpers, rule } = ctx;
|
|
23442
23544
|
|
|
23443
23545
|
const ariaHelpers = helpers && helpers.aria ? helpers.aria : null;
|
|
23444
23546
|
if (!ariaHelpers) {
|
|
@@ -23502,7 +23604,7 @@ if (isAccTreeEligible) {
|
|
|
23502
23604
|
};
|
|
23503
23605
|
|
|
23504
23606
|
const globalSet = new Set(GLOBAL_ATTRS);
|
|
23505
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role]'
|
|
23607
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role]') : helpers.queryAll('[role]');
|
|
23506
23608
|
|
|
23507
23609
|
const occurrences = [];
|
|
23508
23610
|
let applicableCount = 0;
|
|
@@ -23567,15 +23669,14 @@ if (isAccTreeEligible) {
|
|
|
23567
23669
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
23568
23670
|
}), applicability: null },
|
|
23569
23671
|
"aria-allowed-role": { run: (function runInPage(ctx) {
|
|
23570
|
-
const { document,
|
|
23571
|
-
const safeRoot = root || document;
|
|
23672
|
+
const { document, helpers, rule } = ctx;
|
|
23572
23673
|
|
|
23573
23674
|
const ariaHelpers = helpers && helpers.aria ? helpers.aria : null;
|
|
23574
23675
|
if (!ariaHelpers) {
|
|
23575
23676
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
23576
23677
|
}
|
|
23577
23678
|
|
|
23578
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role]'
|
|
23679
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role]') : helpers.queryAll('[role]');
|
|
23579
23680
|
|
|
23580
23681
|
const occurrences = [];
|
|
23581
23682
|
let applicableCount = 0;
|
|
@@ -23622,8 +23723,7 @@ if (isAccTreeEligible) {
|
|
|
23622
23723
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
23623
23724
|
}), applicability: null },
|
|
23624
23725
|
"aria-braille-equivalent": { run: (function runInPage(ctx) {
|
|
23625
|
-
const { document,
|
|
23626
|
-
const safeRoot = root || document;
|
|
23726
|
+
const { document, helpers, rule } = ctx;
|
|
23627
23727
|
|
|
23628
23728
|
function trim(v) { return (v == null ? '' : String(v)).trim(); }
|
|
23629
23729
|
|
|
@@ -23644,8 +23744,8 @@ if (isAccTreeEligible) {
|
|
|
23644
23744
|
}
|
|
23645
23745
|
|
|
23646
23746
|
const nodes = helpers.queryAllSmart
|
|
23647
|
-
? helpers.queryAllSmart('[aria-braillelabel], [aria-brailleroledescription]'
|
|
23648
|
-
: helpers.queryAll('[aria-braillelabel], [aria-brailleroledescription]'
|
|
23747
|
+
? helpers.queryAllSmart('[aria-braillelabel], [aria-brailleroledescription]')
|
|
23748
|
+
: helpers.queryAll('[aria-braillelabel], [aria-brailleroledescription]');
|
|
23649
23749
|
|
|
23650
23750
|
const occurrences = [];
|
|
23651
23751
|
let applicableCount = 0;
|
|
@@ -23706,14 +23806,13 @@ if (isAccTreeEligible) {
|
|
|
23706
23806
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
23707
23807
|
}), applicability: null },
|
|
23708
23808
|
"aria-checked-state-mismatch": { run: (function runInPage(ctx) {
|
|
23709
|
-
const { document,
|
|
23710
|
-
const safeRoot = root || document;
|
|
23809
|
+
const { document, helpers, rule } = ctx;
|
|
23711
23810
|
|
|
23712
23811
|
function trim(v) { return (v == null ? '' : String(v)).trim(); }
|
|
23713
23812
|
|
|
23714
23813
|
const nodes = helpers.queryAllSmart
|
|
23715
|
-
? helpers.queryAllSmart('input[type="checkbox"][aria-checked], input[type="radio"][aria-checked]'
|
|
23716
|
-
: helpers.queryAll('input[type="checkbox"][aria-checked], input[type="radio"][aria-checked]'
|
|
23814
|
+
? helpers.queryAllSmart('input[type="checkbox"][aria-checked], input[type="radio"][aria-checked]')
|
|
23815
|
+
: helpers.queryAll('input[type="checkbox"][aria-checked], input[type="radio"][aria-checked]');
|
|
23717
23816
|
|
|
23718
23817
|
const occurrences = [];
|
|
23719
23818
|
let applicableCount = 0;
|
|
@@ -23775,16 +23874,15 @@ if (isAccTreeEligible) {
|
|
|
23775
23874
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
23776
23875
|
}), applicability: null },
|
|
23777
23876
|
"aria-conditional-attr": { run: (function runInPage(ctx) {
|
|
23778
|
-
const { document,
|
|
23779
|
-
const safeRoot = root || document;
|
|
23877
|
+
const { document, helpers, rule } = ctx;
|
|
23780
23878
|
|
|
23781
23879
|
function trim(v) { return (v == null ? '' : String(v)).trim(); }
|
|
23782
23880
|
|
|
23783
23881
|
const TRUTHY_INVALID_VALUES = new Set(['true', 'grammar', 'spelling']);
|
|
23784
23882
|
|
|
23785
23883
|
const nodes = helpers.queryAllSmart
|
|
23786
|
-
? helpers.queryAllSmart('[aria-errormessage]'
|
|
23787
|
-
: helpers.queryAll('[aria-errormessage]'
|
|
23884
|
+
? helpers.queryAllSmart('[aria-errormessage]')
|
|
23885
|
+
: helpers.queryAll('[aria-errormessage]');
|
|
23788
23886
|
|
|
23789
23887
|
const occurrences = [];
|
|
23790
23888
|
let applicableCount = 0;
|
|
@@ -23829,15 +23927,14 @@ if (isAccTreeEligible) {
|
|
|
23829
23927
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
23830
23928
|
}), applicability: null },
|
|
23831
23929
|
"aria-deprecated-role": { run: (function runInPage(ctx) {
|
|
23832
|
-
const { document,
|
|
23833
|
-
const safeRoot = root || document;
|
|
23930
|
+
const { document, helpers, rule } = ctx;
|
|
23834
23931
|
|
|
23835
23932
|
const ariaHelpers = helpers && helpers.aria ? helpers.aria : null;
|
|
23836
23933
|
if (!ariaHelpers) {
|
|
23837
23934
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
23838
23935
|
}
|
|
23839
23936
|
|
|
23840
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role]'
|
|
23937
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role]') : helpers.queryAll('[role]');
|
|
23841
23938
|
|
|
23842
23939
|
const occurrences = [];
|
|
23843
23940
|
let applicableCount = 0;
|
|
@@ -24349,8 +24446,7 @@ if (isAccTreeEligible) {
|
|
|
24349
24446
|
return { ruleId: rule.ruleId, outcome: 'fail', severity: rule.defaultSeverity || 'minor', occurrences };
|
|
24350
24447
|
}), applicability: null },
|
|
24351
24448
|
"aria-prohibited-attr": { run: (function runInPage(ctx) {
|
|
24352
|
-
const { document,
|
|
24353
|
-
const safeRoot = root || document;
|
|
24449
|
+
const { document, helpers, rule } = ctx;
|
|
24354
24450
|
|
|
24355
24451
|
const ariaHelpers = helpers && helpers.aria ? helpers.aria : null;
|
|
24356
24452
|
if (!ariaHelpers) {
|
|
@@ -24370,7 +24466,7 @@ if (isAccTreeEligible) {
|
|
|
24370
24466
|
|
|
24371
24467
|
const PROHIBITED_NAMING_ATTRS = ['aria-label', 'aria-labelledby'];
|
|
24372
24468
|
|
|
24373
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role]'
|
|
24469
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role]') : helpers.queryAll('[role]');
|
|
24374
24470
|
|
|
24375
24471
|
const occurrences = [];
|
|
24376
24472
|
let applicableCount = 0;
|
|
@@ -24421,8 +24517,7 @@ if (isAccTreeEligible) {
|
|
|
24421
24517
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
24422
24518
|
}), applicability: null },
|
|
24423
24519
|
"aria-prohibited-children": { run: (function runInPage(ctx) {
|
|
24424
|
-
const { document,
|
|
24425
|
-
const safeRoot = root || document;
|
|
24520
|
+
const { document, helpers, rule } = ctx;
|
|
24426
24521
|
|
|
24427
24522
|
const ariaHelpers = helpers && helpers.aria ? helpers.aria : null;
|
|
24428
24523
|
if (!ariaHelpers) {
|
|
@@ -24511,7 +24606,7 @@ if (isAccTreeEligible) {
|
|
|
24511
24606
|
}
|
|
24512
24607
|
}
|
|
24513
24608
|
|
|
24514
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role]'
|
|
24609
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role]') : helpers.queryAll('[role]');
|
|
24515
24610
|
|
|
24516
24611
|
const occurrences = [];
|
|
24517
24612
|
let applicableCount = 0;
|
|
@@ -24583,8 +24678,7 @@ if (isAccTreeEligible) {
|
|
|
24583
24678
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
24584
24679
|
}), applicability: null },
|
|
24585
24680
|
"aria-required-attr": { run: (function runInPage(ctx) {
|
|
24586
|
-
const { document,
|
|
24587
|
-
const safeRoot = root || document;
|
|
24681
|
+
const { document, helpers, rule } = ctx;
|
|
24588
24682
|
|
|
24589
24683
|
const ariaHelpers = helpers && helpers.aria ? helpers.aria : null;
|
|
24590
24684
|
if (!ariaHelpers) {
|
|
@@ -24608,7 +24702,7 @@ if (isAccTreeEligible) {
|
|
|
24608
24702
|
return v != null && String(v).trim().toLowerCase() === 'true';
|
|
24609
24703
|
}
|
|
24610
24704
|
|
|
24611
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role]'
|
|
24705
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role]') : helpers.queryAll('[role]');
|
|
24612
24706
|
|
|
24613
24707
|
const occurrences = [];
|
|
24614
24708
|
let applicableCount = 0;
|
|
@@ -24665,8 +24759,7 @@ if (isAccTreeEligible) {
|
|
|
24665
24759
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
24666
24760
|
}), applicability: null },
|
|
24667
24761
|
"aria-required-children": { run: (function runInPage(ctx) {
|
|
24668
|
-
const { document,
|
|
24669
|
-
const safeRoot = root || document;
|
|
24762
|
+
const { document, helpers, rule } = ctx;
|
|
24670
24763
|
|
|
24671
24764
|
const ariaHelpers = helpers && helpers.aria ? helpers.aria : null;
|
|
24672
24765
|
if (!ariaHelpers) {
|
|
@@ -24697,7 +24790,7 @@ if (isAccTreeEligible) {
|
|
|
24697
24790
|
// ("runInPage MUST be self-contained").
|
|
24698
24791
|
const CANDIDATE_SELECTOR = '[role], li, option, tr, td, th, thead, tbody, tfoot, ul, ol, table, select, input[type="radio"]';
|
|
24699
24792
|
|
|
24700
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role]'
|
|
24793
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role]') : helpers.queryAll('[role]');
|
|
24701
24794
|
|
|
24702
24795
|
const occurrences = [];
|
|
24703
24796
|
let applicableCount = 0;
|
|
@@ -24856,8 +24949,7 @@ if (isAccTreeEligible) {
|
|
|
24856
24949
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
24857
24950
|
}), applicability: null },
|
|
24858
24951
|
"aria-required-parent": { run: (function runInPage(ctx) {
|
|
24859
|
-
const { document,
|
|
24860
|
-
const safeRoot = root || document;
|
|
24952
|
+
const { document, helpers, rule } = ctx;
|
|
24861
24953
|
|
|
24862
24954
|
const ariaHelpers = helpers && helpers.aria ? helpers.aria : null;
|
|
24863
24955
|
if (!ariaHelpers) {
|
|
@@ -24943,7 +25035,7 @@ if (isAccTreeEligible) {
|
|
|
24943
25035
|
const idTok = elId && String(elId).trim();
|
|
24944
25036
|
if (!idTok) return false;
|
|
24945
25037
|
|
|
24946
|
-
const owners = helpers.queryAllSmart ? helpers.queryAllSmart('[aria-owns]'
|
|
25038
|
+
const owners = helpers.queryAllSmart ? helpers.queryAllSmart('[aria-owns]') : helpers.queryAll('[aria-owns]');
|
|
24947
25039
|
for (const owner of owners) {
|
|
24948
25040
|
if (!owner || !owner.getAttribute) continue;
|
|
24949
25041
|
const ownsAttr = owner.getAttribute('aria-owns') || '';
|
|
@@ -24956,7 +25048,7 @@ if (isAccTreeEligible) {
|
|
|
24956
25048
|
return false;
|
|
24957
25049
|
}
|
|
24958
25050
|
|
|
24959
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role]'
|
|
25051
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role]') : helpers.queryAll('[role]');
|
|
24960
25052
|
|
|
24961
25053
|
const occurrences = [];
|
|
24962
25054
|
let applicableCount = 0;
|
|
@@ -25169,15 +25261,14 @@ if (isAccTreeEligible) {
|
|
|
25169
25261
|
return { ruleId: rule.ruleId, outcome: 'fail', severity: rule.defaultSeverity || 'minor', occurrences };
|
|
25170
25262
|
}), applicability: null },
|
|
25171
25263
|
"aria-roles-valid": { run: (function runInPage(ctx) {
|
|
25172
|
-
const { document,
|
|
25173
|
-
const safeRoot = root || document;
|
|
25264
|
+
const { document, helpers, rule } = ctx;
|
|
25174
25265
|
|
|
25175
25266
|
const ariaHelpers = helpers && helpers.aria ? helpers.aria : null;
|
|
25176
25267
|
if (!ariaHelpers) {
|
|
25177
25268
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
25178
25269
|
}
|
|
25179
25270
|
|
|
25180
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role]'
|
|
25271
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role]') : helpers.queryAll('[role]');
|
|
25181
25272
|
|
|
25182
25273
|
const occurrences = [];
|
|
25183
25274
|
let applicableCount = 0;
|
|
@@ -25232,13 +25323,12 @@ if (isAccTreeEligible) {
|
|
|
25232
25323
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
25233
25324
|
}), applicability: null },
|
|
25234
25325
|
"aria-text": { run: (function runInPage(ctx) {
|
|
25235
|
-
const { document,
|
|
25236
|
-
const safeRoot = root || document;
|
|
25326
|
+
const { document, helpers, rule } = ctx;
|
|
25237
25327
|
|
|
25238
25328
|
const FOCUSABLE_DESCENDANT_SELECTOR =
|
|
25239
25329
|
'a[href], button, input, select, textarea, [tabindex], iframe, [contenteditable]:not([contenteditable="false"])';
|
|
25240
25330
|
|
|
25241
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role="text"]'
|
|
25331
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role="text"]') : helpers.queryAll('[role="text"]');
|
|
25242
25332
|
|
|
25243
25333
|
const occurrences = [];
|
|
25244
25334
|
let applicableCount = 0;
|
|
@@ -25292,15 +25382,14 @@ if (isAccTreeEligible) {
|
|
|
25292
25382
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
25293
25383
|
}), applicability: null },
|
|
25294
25384
|
"aria-valid-attr": { run: (function runInPage(ctx) {
|
|
25295
|
-
const { document,
|
|
25296
|
-
const safeRoot = root || document;
|
|
25385
|
+
const { document, helpers, rule } = ctx;
|
|
25297
25386
|
|
|
25298
25387
|
const ariaHelpers = helpers && helpers.aria ? helpers.aria : null;
|
|
25299
25388
|
if (!ariaHelpers) {
|
|
25300
25389
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
25301
25390
|
}
|
|
25302
25391
|
|
|
25303
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('*'
|
|
25392
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('*') : helpers.queryAll('*');
|
|
25304
25393
|
|
|
25305
25394
|
const occurrences = [];
|
|
25306
25395
|
let applicableCount = 0;
|
|
@@ -25354,15 +25443,14 @@ if (isAccTreeEligible) {
|
|
|
25354
25443
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
25355
25444
|
}), applicability: null },
|
|
25356
25445
|
"aria-valid-attr-value": { run: (function runInPage(ctx) {
|
|
25357
|
-
const { document,
|
|
25358
|
-
const safeRoot = root || document;
|
|
25446
|
+
const { document, helpers, rule } = ctx;
|
|
25359
25447
|
|
|
25360
25448
|
const ariaHelpers = helpers && helpers.aria ? helpers.aria : null;
|
|
25361
25449
|
if (!ariaHelpers) {
|
|
25362
25450
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
25363
25451
|
}
|
|
25364
25452
|
|
|
25365
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('*'
|
|
25453
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('*') : helpers.queryAll('*');
|
|
25366
25454
|
|
|
25367
25455
|
const occurrences = [];
|
|
25368
25456
|
let applicableCount = 0;
|
|
@@ -25419,8 +25507,7 @@ if (isAccTreeEligible) {
|
|
|
25419
25507
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
25420
25508
|
}), applicability: null },
|
|
25421
25509
|
"autocomplete-valid": { run: (function runInPage(ctx) {
|
|
25422
|
-
const { document,
|
|
25423
|
-
const safeRoot = root || document;
|
|
25510
|
+
const { document, helpers, rule } = ctx;
|
|
25424
25511
|
|
|
25425
25512
|
// Declared inside runInPage — see scripts/build-core.js header
|
|
25426
25513
|
// ("runInPage MUST be self-contained").
|
|
@@ -25455,7 +25542,7 @@ if (isAccTreeEligible) {
|
|
|
25455
25542
|
return FIELD_NAMES.has(remaining[0]);
|
|
25456
25543
|
}
|
|
25457
25544
|
|
|
25458
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('input, select, textarea'
|
|
25545
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('input, select, textarea') : helpers.queryAll('input, select, textarea');
|
|
25459
25546
|
|
|
25460
25547
|
const occurrences = [];
|
|
25461
25548
|
let applicableCount = 0;
|
|
@@ -25498,12 +25585,11 @@ if (isAccTreeEligible) {
|
|
|
25498
25585
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
25499
25586
|
}), applicability: null },
|
|
25500
25587
|
"avoid-inline-spacing": { run: (function runInPage(ctx) {
|
|
25501
|
-
const { document,
|
|
25502
|
-
const safeRoot = root || document;
|
|
25588
|
+
const { document, helpers, rule } = ctx;
|
|
25503
25589
|
|
|
25504
25590
|
const SPACING_PROPS = ['line-height', 'letter-spacing', 'word-spacing'];
|
|
25505
25591
|
|
|
25506
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[style]'
|
|
25592
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[style]') : helpers.queryAll('[style]');
|
|
25507
25593
|
|
|
25508
25594
|
const occurrences = [];
|
|
25509
25595
|
let applicableCount = 0;
|
|
@@ -25556,11 +25642,10 @@ if (isAccTreeEligible) {
|
|
|
25556
25642
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
25557
25643
|
}), applicability: null },
|
|
25558
25644
|
"binary-control-name-present": { run: (function runInPage(ctx) {
|
|
25559
|
-
const { document,
|
|
25645
|
+
const { document, helpers, rule } = ctx;
|
|
25560
25646
|
const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
|
|
25561
25647
|
? helpers.getEligibilityInfo
|
|
25562
25648
|
: null;
|
|
25563
|
-
const safeRoot = root || document;
|
|
25564
25649
|
|
|
25565
25650
|
|
|
25566
25651
|
function normalizeWs(s) {
|
|
@@ -25692,7 +25777,7 @@ if (isAccTreeEligible) {
|
|
|
25692
25777
|
let applicableCount = 0;
|
|
25693
25778
|
|
|
25694
25779
|
const selector = 'input[type="checkbox"], input[type="radio"], [role="checkbox"], [role="radio"], [role="switch"]';
|
|
25695
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
25780
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
25696
25781
|
|
|
25697
25782
|
|
|
25698
25783
|
// Precompute label[for] associations once for speed/determinism.
|
|
@@ -25824,8 +25909,7 @@ if (isAccTreeEligible) {
|
|
|
25824
25909
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
25825
25910
|
}), applicability: null },
|
|
25826
25911
|
"button-name-present": { run: (function runInPage(ctx) {
|
|
25827
|
-
const { document,
|
|
25828
|
-
const safeRoot = root || document;
|
|
25912
|
+
const { document, helpers, rule } = ctx;
|
|
25829
25913
|
|
|
25830
25914
|
const occurrences = [];
|
|
25831
25915
|
let applicableCount = 0;
|
|
@@ -25862,7 +25946,7 @@ if (isAccTreeEligible) {
|
|
|
25862
25946
|
}
|
|
25863
25947
|
|
|
25864
25948
|
const selector = 'button, input[type="button"], input[type="submit"], input[type="reset"], [role="button"]';
|
|
25865
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
25949
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
25866
25950
|
|
|
25867
25951
|
for (const el of nodes) {
|
|
25868
25952
|
// isAccTreeEligible returns { eligible, reasons }, not a boolean.
|
|
@@ -25874,6 +25958,7 @@ if (isAccTreeEligible) {
|
|
|
25874
25958
|
|
|
25875
25959
|
const tag = (el.tagName || '').toLowerCase();
|
|
25876
25960
|
const role = el.getAttribute ? el.getAttribute('role') : null;
|
|
25961
|
+
const roleNorm = normalizeWs(role).toLowerCase();
|
|
25877
25962
|
|
|
25878
25963
|
const nameInfo = helpers.getAccessibleNameInfo ? helpers.getAccessibleNameInfo(el, ctx) : null;
|
|
25879
25964
|
|
|
@@ -25889,7 +25974,17 @@ if (isAccTreeEligible) {
|
|
|
25889
25974
|
inputValueName = getInputButtonValueName(el);
|
|
25890
25975
|
}
|
|
25891
25976
|
|
|
25892
|
-
|
|
25977
|
+
// A native <button> (or [role="button"]) whose role has been overridden to
|
|
25978
|
+
// one of these roles is no longer semantically a button — per the WAI-ARIA
|
|
25979
|
+
// Accessible Name and Description Computation spec these roles are
|
|
25980
|
+
// name-from-author-only, and their rendered content represents a VALUE,
|
|
25981
|
+
// not a NAME (mirrors axe-core's controlValueRoles, verified against its
|
|
25982
|
+
// source). Found on a real page (Spotify's "sort by" control): a
|
|
25983
|
+
// <button role="combobox">List</button> where "List" is the combobox's
|
|
25984
|
+
// currently selected value, not a label for what the combobox is —
|
|
25985
|
+
// crediting it as the accessible name masked a real missing-name bug.
|
|
25986
|
+
const VALUE_ROLES = ['textbox', 'progressbar', 'scrollbar', 'slider', 'spinbutton', 'combobox', 'listbox'];
|
|
25987
|
+
const isContentNameCandidate = (tag === 'button' || role === 'button') && !VALUE_ROLES.includes(roleNorm);
|
|
25893
25988
|
const contentName =
|
|
25894
25989
|
(!trustedProgrammaticName && !inputValueName && isContentNameCandidate)
|
|
25895
25990
|
? getConservativeSubtreeText(el)
|
|
@@ -25949,12 +26044,41 @@ if (isAccTreeEligible) {
|
|
|
25949
26044
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
25950
26045
|
}
|
|
25951
26046
|
|
|
25952
|
-
function
|
|
26047
|
+
const isAccTreeEligible = helpers && typeof helpers.isAccTreeEligible === 'function' ? helpers.isAccTreeEligible : null;
|
|
26048
|
+
|
|
26049
|
+
function isExposedToAt(el) {
|
|
26050
|
+
if (!isAccTreeEligible) return true;
|
|
25953
26051
|
try {
|
|
25954
|
-
|
|
26052
|
+
const r = isAccTreeEligible(el, ctx);
|
|
26053
|
+
if (typeof r === 'boolean') return r;
|
|
26054
|
+
return !!(r && r.eligible);
|
|
25955
26055
|
} catch {
|
|
25956
|
-
return
|
|
26056
|
+
return true;
|
|
26057
|
+
}
|
|
26058
|
+
}
|
|
26059
|
+
|
|
26060
|
+
function queryAll(selector) {
|
|
26061
|
+
try {
|
|
26062
|
+
return helpers && typeof helpers.queryAllSmart === 'function'
|
|
26063
|
+
? helpers.queryAllSmart(selector)
|
|
26064
|
+
: document.querySelectorAll(selector);
|
|
26065
|
+
} catch {
|
|
26066
|
+
return [];
|
|
26067
|
+
}
|
|
26068
|
+
}
|
|
26069
|
+
|
|
26070
|
+
// Filters through isAccTreeEligible (hidden/aria-hidden/display:none/inert
|
|
26071
|
+
// don't count as "a bypass mechanism is present") -- see page-has-heading-
|
|
26072
|
+
// one-manual.js's identical fix for the real-world trigger (CDC's flu
|
|
26073
|
+
// page, 2026-07-30: its only <h1> sits inside a display:none ancestor,
|
|
26074
|
+
// unreachable by sighted and screen reader users alike). A fully
|
|
26075
|
+
// non-rendered <main>/heading was previously credited here too, wrongly
|
|
26076
|
+
// returning `pass` for a page with zero actual bypass mechanisms.
|
|
26077
|
+
function hasMainLandmark() {
|
|
26078
|
+
for (const el of queryAll('main, [role="main"]')) {
|
|
26079
|
+
if (el && isExposedToAt(el)) return true;
|
|
25957
26080
|
}
|
|
26081
|
+
return false;
|
|
25958
26082
|
}
|
|
25959
26083
|
|
|
25960
26084
|
function hasWorkingAnchorLink() {
|
|
@@ -25996,11 +26120,10 @@ if (isAccTreeEligible) {
|
|
|
25996
26120
|
}
|
|
25997
26121
|
|
|
25998
26122
|
function hasHeading() {
|
|
25999
|
-
|
|
26000
|
-
|
|
26001
|
-
} catch {
|
|
26002
|
-
return false;
|
|
26123
|
+
for (const el of queryAll('h1, h2, h3, h4, h5, h6, [role="heading"]')) {
|
|
26124
|
+
if (el && isExposedToAt(el)) return true;
|
|
26003
26125
|
}
|
|
26126
|
+
return false;
|
|
26004
26127
|
}
|
|
26005
26128
|
|
|
26006
26129
|
const mainLandmark = hasMainLandmark();
|
|
@@ -26258,11 +26381,10 @@ if (isAccTreeEligible) {
|
|
|
26258
26381
|
return { ruleId: rule.ruleId, outcome: 'cantTell', severity: 'minor', occurrences };
|
|
26259
26382
|
}), applicability: null },
|
|
26260
26383
|
"combobox-name-present": { run: (function runInPage(ctx) {
|
|
26261
|
-
const { document,
|
|
26384
|
+
const { document, helpers, rule } = ctx;
|
|
26262
26385
|
const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
|
|
26263
26386
|
? helpers.getEligibilityInfo
|
|
26264
26387
|
: null;
|
|
26265
|
-
const safeRoot = root || document;
|
|
26266
26388
|
|
|
26267
26389
|
|
|
26268
26390
|
function normalizeWs(s) {
|
|
@@ -26394,7 +26516,7 @@ if (isAccTreeEligible) {
|
|
|
26394
26516
|
let applicableCount = 0;
|
|
26395
26517
|
|
|
26396
26518
|
const selector = '[role="combobox"]';
|
|
26397
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
26519
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
26398
26520
|
|
|
26399
26521
|
// Precompute label[for] map for combobox elements that are labelable
|
|
26400
26522
|
// native form controls (e.g. <input role="combobox">).
|
|
@@ -28121,14 +28243,13 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
28121
28243
|
return { ruleId: rule.ruleId, outcome: 'fail', severity: rule.defaultSeverity || 'serious', occurrences };
|
|
28122
28244
|
}), applicability: null },
|
|
28123
28245
|
"definition-list-children-valid": { run: (function runInPage(ctx) {
|
|
28124
|
-
const { document,
|
|
28125
|
-
const safeRoot = root || document;
|
|
28246
|
+
const { document, helpers, rule } = ctx;
|
|
28126
28247
|
|
|
28127
28248
|
// Declared inside runInPage — see scripts/build-core.js header
|
|
28128
28249
|
// ("runInPage MUST be self-contained").
|
|
28129
28250
|
const PASSTHROUGH_TAGS = new Set(['dt', 'dd', 'script', 'template', 'style']);
|
|
28130
28251
|
|
|
28131
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('dl'
|
|
28252
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('dl') : helpers.queryAll('dl');
|
|
28132
28253
|
|
|
28133
28254
|
const occurrences = [];
|
|
28134
28255
|
let applicableCount = 0;
|
|
@@ -28212,10 +28333,9 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
28212
28333
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
28213
28334
|
}), applicability: null },
|
|
28214
28335
|
"deprecated-elements-not-used": { run: (function runInPage(ctx) {
|
|
28215
|
-
const { document,
|
|
28216
|
-
const safeRoot = root || document;
|
|
28336
|
+
const { document, helpers, rule } = ctx;
|
|
28217
28337
|
|
|
28218
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('blink, marquee'
|
|
28338
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('blink, marquee') : helpers.queryAll('blink, marquee');
|
|
28219
28339
|
|
|
28220
28340
|
const occurrences = [];
|
|
28221
28341
|
let applicableCount = 0;
|
|
@@ -28254,11 +28374,10 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
28254
28374
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
28255
28375
|
}), applicability: null },
|
|
28256
28376
|
"dialog-name-present": { run: (function runInPage(ctx) {
|
|
28257
|
-
const { document,
|
|
28377
|
+
const { document, helpers, rule } = ctx;
|
|
28258
28378
|
const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
|
|
28259
28379
|
? helpers.getEligibilityInfo
|
|
28260
28380
|
: null;
|
|
28261
|
-
const safeRoot = root || document;
|
|
28262
28381
|
|
|
28263
28382
|
|
|
28264
28383
|
function normalizeWs(s) {
|
|
@@ -28372,7 +28491,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
28372
28491
|
let applicableCount = 0;
|
|
28373
28492
|
|
|
28374
28493
|
const selector = '[role="dialog"],[role="alertdialog"]';
|
|
28375
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
28494
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
28376
28495
|
|
|
28377
28496
|
|
|
28378
28497
|
function evaluate(el) {
|
|
@@ -28437,10 +28556,9 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
28437
28556
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
28438
28557
|
}), applicability: null },
|
|
28439
28558
|
"dlitem-parent-valid": { run: (function runInPage(ctx) {
|
|
28440
|
-
const { document,
|
|
28441
|
-
const safeRoot = root || document;
|
|
28559
|
+
const { document, helpers, rule } = ctx;
|
|
28442
28560
|
|
|
28443
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('dt, dd'
|
|
28561
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('dt, dd') : helpers.queryAll('dt, dd');
|
|
28444
28562
|
|
|
28445
28563
|
const occurrences = [];
|
|
28446
28564
|
let applicableCount = 0;
|
|
@@ -29092,8 +29210,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
29092
29210
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
29093
29211
|
}), applicability: null },
|
|
29094
29212
|
"focus-order-semantics": { run: (function runInPage(ctx) {
|
|
29095
|
-
const { document,
|
|
29096
|
-
const safeRoot = root || document;
|
|
29213
|
+
const { document, helpers, rule } = ctx;
|
|
29097
29214
|
|
|
29098
29215
|
const NON_INTERACTIVE_ROLES = new Set([
|
|
29099
29216
|
'generic', 'group', 'text', 'presentation', 'none', 'img', 'heading',
|
|
@@ -29103,7 +29220,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
29103
29220
|
'definition', 'paragraph', 'caption', 'status', 'alert', 'log', 'tooltip'
|
|
29104
29221
|
]);
|
|
29105
29222
|
|
|
29106
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[tabindex][role]'
|
|
29223
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[tabindex][role]') : helpers.queryAll('[tabindex][role]');
|
|
29107
29224
|
|
|
29108
29225
|
const occurrences = [];
|
|
29109
29226
|
let applicableCount = 0;
|
|
@@ -29744,11 +29861,10 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
29744
29861
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [], data: { details: { metrics } } };
|
|
29745
29862
|
}), applicability: null },
|
|
29746
29863
|
"form-control-single-label": { run: (function runInPage(ctx) {
|
|
29747
|
-
const { document,
|
|
29748
|
-
const safeRoot = root || document;
|
|
29864
|
+
const { document, helpers, rule } = ctx;
|
|
29749
29865
|
|
|
29750
29866
|
const selector = 'input:not([type="hidden"]):not([type="submit"]):not([type="reset"]):not([type="button"]):not([type="image"]),select,textarea';
|
|
29751
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
29867
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
29752
29868
|
|
|
29753
29869
|
// Build a for-value -> label[] map once (avoids per-control dynamic
|
|
29754
29870
|
// attribute-selector construction / CSS.escape, which is not guaranteed
|
|
@@ -30026,14 +30142,13 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
30026
30142
|
return { ruleId: rule.ruleId, outcome: 'fail', severity: rule.defaultSeverity || 'serious', occurrences };
|
|
30027
30143
|
}), applicability: null },
|
|
30028
30144
|
"identical-links-same-purpose": { run: (function runInPage(ctx) {
|
|
30029
|
-
const { document,
|
|
30030
|
-
const safeRoot = root || document;
|
|
30145
|
+
const { document, helpers, rule } = ctx;
|
|
30031
30146
|
|
|
30032
30147
|
function normName(s) {
|
|
30033
30148
|
return (s == null ? '' : String(s)).replace(/\s+/g, ' ').trim().toLowerCase();
|
|
30034
30149
|
}
|
|
30035
30150
|
|
|
30036
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('a[href]'
|
|
30151
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('a[href]') : helpers.queryAll('a[href]');
|
|
30037
30152
|
|
|
30038
30153
|
const groups = new Map(); // normName -> [{ el, href }]
|
|
30039
30154
|
let applicableCount = 0;
|
|
@@ -30113,8 +30228,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
30113
30228
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
30114
30229
|
}), applicability: null },
|
|
30115
30230
|
"iframe-focusable-content": { run: (function runInPage(ctx) {
|
|
30116
|
-
const { document,
|
|
30117
|
-
const safeRoot = root || document;
|
|
30231
|
+
const { document, helpers, rule } = ctx;
|
|
30118
30232
|
|
|
30119
30233
|
function hasFocusableCandidate(doc) {
|
|
30120
30234
|
if (!doc || !doc.querySelectorAll) return false;
|
|
@@ -30146,7 +30260,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
30146
30260
|
return !Number.isNaN(n) && n < 0;
|
|
30147
30261
|
}
|
|
30148
30262
|
|
|
30149
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('iframe, frame'
|
|
30263
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('iframe, frame') : helpers.queryAll('iframe, frame');
|
|
30150
30264
|
|
|
30151
30265
|
const occurrences = [];
|
|
30152
30266
|
let applicableCount = 0;
|
|
@@ -30196,10 +30310,9 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
30196
30310
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
30197
30311
|
}), applicability: null },
|
|
30198
30312
|
"iframe-name-present": { run: (function runInPage(ctx) {
|
|
30199
|
-
const { document,
|
|
30200
|
-
const safeRoot = root || document;
|
|
30313
|
+
const { document, helpers, rule } = ctx;
|
|
30201
30314
|
|
|
30202
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('iframe, frame'
|
|
30315
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('iframe, frame') : helpers.queryAll('iframe, frame');
|
|
30203
30316
|
|
|
30204
30317
|
const occurrences = [];
|
|
30205
30318
|
let applicableCount = 0;
|
|
@@ -30251,10 +30364,9 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
30251
30364
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
30252
30365
|
}), applicability: null },
|
|
30253
30366
|
"iframe-title-unique": { run: (function runInPage(ctx) {
|
|
30254
|
-
const { document,
|
|
30255
|
-
const safeRoot = root || document;
|
|
30367
|
+
const { document, helpers, rule } = ctx;
|
|
30256
30368
|
|
|
30257
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('iframe, frame'
|
|
30369
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('iframe, frame') : helpers.queryAll('iframe, frame');
|
|
30258
30370
|
|
|
30259
30371
|
const groups = new Map(); // trimmed title -> elements[]
|
|
30260
30372
|
let applicableCount = 0;
|
|
@@ -30309,8 +30421,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
30309
30421
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
30310
30422
|
}), applicability: null },
|
|
30311
30423
|
"image-redundant-alt": { run: (function runInPage(ctx) {
|
|
30312
|
-
const { document,
|
|
30313
|
-
const safeRoot = root || document;
|
|
30424
|
+
const { document, helpers, rule } = ctx;
|
|
30314
30425
|
|
|
30315
30426
|
function normalizeWs(s) {
|
|
30316
30427
|
return String(s || '').replace(/\s+/g, ' ').trim();
|
|
@@ -30329,7 +30440,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
30329
30440
|
return normalizeWs(text);
|
|
30330
30441
|
}
|
|
30331
30442
|
|
|
30332
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('img[alt]'
|
|
30443
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('img[alt]') : helpers.queryAll('img[alt]');
|
|
30333
30444
|
|
|
30334
30445
|
const occurrences = [];
|
|
30335
30446
|
let applicableCount = 0;
|
|
@@ -31117,8 +31228,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
31117
31228
|
return { ruleId: rule.ruleId, outcome: 'cantTell', severity: 'minor', occurrences };
|
|
31118
31229
|
}), applicability: null },
|
|
31119
31230
|
"label-in-name": { run: (function runInPage(ctx) {
|
|
31120
|
-
const { document,
|
|
31121
|
-
const safeRoot = root || document;
|
|
31231
|
+
const { document, helpers, rule } = ctx;
|
|
31122
31232
|
|
|
31123
31233
|
const occurrences = [];
|
|
31124
31234
|
let applicableCount = 0;
|
|
@@ -31127,7 +31237,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
31127
31237
|
// NOTE: aria-hidden is intentionally NOT excluded here; it does not affect visual rendering.
|
|
31128
31238
|
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])';
|
|
31129
31239
|
|
|
31130
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
31240
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
31131
31241
|
|
|
31132
31242
|
function norm(s) {
|
|
31133
31243
|
const v = (s == null ? '' : String(s));
|
|
@@ -31265,11 +31375,11 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
31265
31375
|
}
|
|
31266
31376
|
|
|
31267
31377
|
// Fallback: label[for=id]. Uses `document` directly rather than
|
|
31268
|
-
// ctx.root
|
|
31269
|
-
//
|
|
31270
|
-
//
|
|
31271
|
-
//
|
|
31272
|
-
//
|
|
31378
|
+
// ctx.root -- label[for] association is a document-wide relationship
|
|
31379
|
+
// (IDs are document-unique), not bounded by whatever contextSelector
|
|
31380
|
+
// region happens to be scanned, and ctx.root is an array (multi-region
|
|
31381
|
+
// contextSelector support), not a single element with its own
|
|
31382
|
+
// .querySelector to call directly.
|
|
31273
31383
|
try {
|
|
31274
31384
|
const idAttribute = control && control.getAttribute ? (control.getAttribute('id') || '') : '';
|
|
31275
31385
|
const key = String(idAttribute || '').trim();
|
|
@@ -31410,11 +31520,10 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
31410
31520
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
31411
31521
|
}), applicability: null },
|
|
31412
31522
|
"label-title-only": { run: (function runInPage(ctx) {
|
|
31413
|
-
const { document,
|
|
31414
|
-
const safeRoot = root || document;
|
|
31523
|
+
const { document, helpers, rule } = ctx;
|
|
31415
31524
|
|
|
31416
31525
|
const selector = 'input:not([type="hidden"]):not([type="submit"]):not([type="reset"]):not([type="button"]):not([type="image"]),select,textarea';
|
|
31417
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
31526
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
31418
31527
|
|
|
31419
31528
|
const labelsByFor = new Map();
|
|
31420
31529
|
const allLabels = document.getElementsByTagName ? document.getElementsByTagName('label') : [];
|
|
@@ -32461,8 +32570,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
32461
32570
|
return { ruleId: rule.ruleId, outcome: 'cantTell', severity: rule.defaultSeverity || 'minor', occurrences };
|
|
32462
32571
|
}), applicability: null },
|
|
32463
32572
|
"link-in-text-block": { run: (function runInPage(ctx) {
|
|
32464
|
-
const { document,
|
|
32465
|
-
const safeRoot = root || document;
|
|
32573
|
+
const { document, helpers, rule, engineOptions } = ctx;
|
|
32466
32574
|
|
|
32467
32575
|
function safeComputedStyle(el) {
|
|
32468
32576
|
try {
|
|
@@ -32508,7 +32616,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
32508
32616
|
const c = helpers && helpers.contrast ? helpers.contrast : null;
|
|
32509
32617
|
|
|
32510
32618
|
const selector = 'a[href]';
|
|
32511
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
32619
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
32512
32620
|
|
|
32513
32621
|
const occurrences = [];
|
|
32514
32622
|
let applicableCount = 0;
|
|
@@ -32608,8 +32716,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
32608
32716
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
32609
32717
|
}), applicability: null },
|
|
32610
32718
|
"link-name-present": { run: (function runInPage(ctx) {
|
|
32611
|
-
const { document,
|
|
32612
|
-
const safeRoot = root || document;
|
|
32719
|
+
const { document, helpers, rule } = ctx;
|
|
32613
32720
|
|
|
32614
32721
|
const occurrences = [];
|
|
32615
32722
|
let applicableCount = 0;
|
|
@@ -32630,7 +32737,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
32630
32737
|
}
|
|
32631
32738
|
|
|
32632
32739
|
const selector = 'a[href], area[href], [role="link"]';
|
|
32633
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
32740
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
32634
32741
|
|
|
32635
32742
|
for (const el of nodes) {
|
|
32636
32743
|
// isAccTreeEligible returns { eligible, reasons }, not a boolean.
|
|
@@ -32643,7 +32750,20 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
32643
32750
|
const nameInfo = helpers.getAccessibleNameInfo ? helpers.getAccessibleNameInfo(el, ctx) : null;
|
|
32644
32751
|
const programmaticName = (nameInfo && typeof nameInfo.value === 'string') ? nameInfo.value : '';
|
|
32645
32752
|
|
|
32646
|
-
|
|
32753
|
+
// A native <a href>/<area href> (or [role="link"]) whose role has been
|
|
32754
|
+
// overridden to one of these roles is no longer semantically a link —
|
|
32755
|
+
// per the WAI-ARIA Accessible Name and Description Computation spec
|
|
32756
|
+
// these roles are name-from-author-only, and their rendered content
|
|
32757
|
+
// represents a VALUE, not a NAME (mirrors axe-core's controlValueRoles,
|
|
32758
|
+
// verified against its source; see button-name-present.js's identical
|
|
32759
|
+
// fix for a real-world example — <button role="combobox">List</button> —
|
|
32760
|
+
// of the same class of bug on a different host tag).
|
|
32761
|
+
const role = el.getAttribute ? el.getAttribute('role') : null;
|
|
32762
|
+
const roleNorm = String(role || '').replace(/\s+/g, ' ').trim().toLowerCase();
|
|
32763
|
+
const VALUE_ROLES = ['textbox', 'progressbar', 'scrollbar', 'slider', 'spinbutton', 'combobox', 'listbox'];
|
|
32764
|
+
const isContentNameCandidate = !VALUE_ROLES.includes(roleNorm);
|
|
32765
|
+
|
|
32766
|
+
const contentName = (programmaticName.trim().length === 0 && isContentNameCandidate) ? getConservativeSubtreeText(el) : '';
|
|
32647
32767
|
const finalName = (programmaticName.trim().length ? programmaticName : contentName).trim();
|
|
32648
32768
|
|
|
32649
32769
|
if (finalName.length === 0) {
|
|
@@ -32695,8 +32815,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
32695
32815
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
32696
32816
|
}), applicability: null },
|
|
32697
32817
|
"link-name-quality": { run: (function runInPage(ctx) {
|
|
32698
|
-
const { document,
|
|
32699
|
-
const safeRoot = root || document;
|
|
32818
|
+
const { document, helpers, rule } = ctx;
|
|
32700
32819
|
|
|
32701
32820
|
const GENERIC_LINK_TEXT = new Set([
|
|
32702
32821
|
'click here', 'here', 'click', 'more', 'more info', 'more information',
|
|
@@ -32715,7 +32834,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
32715
32834
|
}
|
|
32716
32835
|
|
|
32717
32836
|
const selector = 'a[href]';
|
|
32718
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
32837
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
32719
32838
|
|
|
32720
32839
|
const occurrences = [];
|
|
32721
32840
|
let applicableCount = 0;
|
|
@@ -32775,14 +32894,13 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
32775
32894
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
32776
32895
|
}), applicability: null },
|
|
32777
32896
|
"list-children-valid": { run: (function runInPage(ctx) {
|
|
32778
|
-
const { document,
|
|
32779
|
-
const safeRoot = root || document;
|
|
32897
|
+
const { document, helpers, rule } = ctx;
|
|
32780
32898
|
|
|
32781
32899
|
// Declared inside runInPage — see scripts/build-core.js header
|
|
32782
32900
|
// ("runInPage MUST be self-contained").
|
|
32783
32901
|
const ALLOWED_CHILD_TAGS = new Set(['li', 'script', 'template']);
|
|
32784
32902
|
|
|
32785
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('ul, ol'
|
|
32903
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('ul, ol') : helpers.queryAll('ul, ol');
|
|
32786
32904
|
|
|
32787
32905
|
const isAccTreeEligible = helpers && typeof helpers.isAccTreeEligible === 'function' ? helpers.isAccTreeEligible : null;
|
|
32788
32906
|
|
|
@@ -32853,11 +32971,10 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
32853
32971
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
32854
32972
|
}), applicability: null },
|
|
32855
32973
|
"listbox-name-present": { run: (function runInPage(ctx) {
|
|
32856
|
-
const { document,
|
|
32974
|
+
const { document, helpers, rule } = ctx;
|
|
32857
32975
|
const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
|
|
32858
32976
|
? helpers.getEligibilityInfo
|
|
32859
32977
|
: null;
|
|
32860
|
-
const safeRoot = root || document;
|
|
32861
32978
|
|
|
32862
32979
|
|
|
32863
32980
|
function normalizeWs(s) {
|
|
@@ -32962,7 +33079,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
32962
33079
|
let applicableCount = 0;
|
|
32963
33080
|
|
|
32964
33081
|
const selector = "[role=\"listbox\"]";
|
|
32965
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
33082
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
32966
33083
|
|
|
32967
33084
|
// Precompute label[for] map for listbox elements that are labelable
|
|
32968
33085
|
// native form controls (e.g. <select multiple role="listbox">).
|
|
@@ -33063,10 +33180,9 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
33063
33180
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
33064
33181
|
}), applicability: null },
|
|
33065
33182
|
"listitem-parent-valid": { run: (function runInPage(ctx) {
|
|
33066
|
-
const { document,
|
|
33067
|
-
const safeRoot = root || document;
|
|
33183
|
+
const { document, helpers, rule } = ctx;
|
|
33068
33184
|
|
|
33069
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('li'
|
|
33185
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('li') : helpers.queryAll('li');
|
|
33070
33186
|
|
|
33071
33187
|
const occurrences = [];
|
|
33072
33188
|
let applicableCount = 0;
|
|
@@ -33495,11 +33611,10 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
33495
33611
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
33496
33612
|
}), applicability: null },
|
|
33497
33613
|
"menuitem-name-present": { run: (function runInPage(ctx) {
|
|
33498
|
-
const { document,
|
|
33614
|
+
const { document, helpers, rule } = ctx;
|
|
33499
33615
|
const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
|
|
33500
33616
|
? helpers.getEligibilityInfo
|
|
33501
33617
|
: null;
|
|
33502
|
-
const safeRoot = root || document;
|
|
33503
33618
|
|
|
33504
33619
|
|
|
33505
33620
|
function normalizeWs(s) {
|
|
@@ -33605,7 +33720,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
33605
33720
|
let applicableCount = 0;
|
|
33606
33721
|
|
|
33607
33722
|
const selector = '[role="menuitem"],[role="menuitemcheckbox"],[role="menuitemradio"]';
|
|
33608
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
33723
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
33609
33724
|
|
|
33610
33725
|
|
|
33611
33726
|
function evaluate(el) {
|
|
@@ -33911,11 +34026,10 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
33911
34026
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
33912
34027
|
}), applicability: null },
|
|
33913
34028
|
"meter-name-present": { run: (function runInPage(ctx) {
|
|
33914
|
-
const { document,
|
|
34029
|
+
const { document, helpers, rule } = ctx;
|
|
33915
34030
|
const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
|
|
33916
34031
|
? helpers.getEligibilityInfo
|
|
33917
34032
|
: null;
|
|
33918
|
-
const safeRoot = root || document;
|
|
33919
34033
|
|
|
33920
34034
|
|
|
33921
34035
|
function normalizeWs(s) {
|
|
@@ -33979,7 +34093,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
33979
34093
|
let applicableCount = 0;
|
|
33980
34094
|
|
|
33981
34095
|
const selector = '[role="meter"]';
|
|
33982
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
34096
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
33983
34097
|
|
|
33984
34098
|
|
|
33985
34099
|
function evaluate(el) {
|
|
@@ -34045,8 +34159,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
34045
34159
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
34046
34160
|
}), applicability: null },
|
|
34047
34161
|
"mouse-only-event-handlers": { run: (function runInPage(ctx) {
|
|
34048
|
-
const { document,
|
|
34049
|
-
const safeRoot = root || document;
|
|
34162
|
+
const { document, helpers, rule } = ctx;
|
|
34050
34163
|
|
|
34051
34164
|
const MOUSE_ONLY_ATTRS = [
|
|
34052
34165
|
'onmouseover', 'onmouseout', 'onmousedown', 'onmouseup',
|
|
@@ -34057,7 +34170,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
34057
34170
|
function trim(v) { return (v == null ? '' : String(v)).trim(); }
|
|
34058
34171
|
|
|
34059
34172
|
const selector = MOUSE_ONLY_ATTRS.map((a) => `[${a}]`).join(', ');
|
|
34060
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
34173
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
34061
34174
|
|
|
34062
34175
|
const occurrences = [];
|
|
34063
34176
|
let applicableCount = 0;
|
|
@@ -34109,8 +34222,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
34109
34222
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
34110
34223
|
}), applicability: null },
|
|
34111
34224
|
"nested-interactive-controls-absent": { run: (function runInPage(ctx) {
|
|
34112
|
-
const { document,
|
|
34113
|
-
const safeRoot = root || document;
|
|
34225
|
+
const { document, helpers, rule } = ctx;
|
|
34114
34226
|
|
|
34115
34227
|
// Declared inside runInPage — see scripts/build-core.js header
|
|
34116
34228
|
// ("runInPage MUST be self-contained").
|
|
@@ -34139,7 +34251,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
34139
34251
|
'[role="treeitem"]'
|
|
34140
34252
|
].join(', ');
|
|
34141
34253
|
|
|
34142
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(INTERACTIVE_SELECTOR
|
|
34254
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(INTERACTIVE_SELECTOR) : helpers.queryAll(INTERACTIVE_SELECTOR);
|
|
34143
34255
|
|
|
34144
34256
|
const occurrences = [];
|
|
34145
34257
|
let applicableCount = 0;
|
|
@@ -34188,10 +34300,9 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
34188
34300
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
34189
34301
|
}), applicability: null },
|
|
34190
34302
|
"no-autoplay-audio": { run: (function runInPage(ctx) {
|
|
34191
|
-
const { document,
|
|
34192
|
-
const safeRoot = root || document;
|
|
34303
|
+
const { document, helpers, rule } = ctx;
|
|
34193
34304
|
|
|
34194
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('audio[autoplay], video[autoplay]'
|
|
34305
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('audio[autoplay], video[autoplay]') : helpers.queryAll('audio[autoplay], video[autoplay]');
|
|
34195
34306
|
|
|
34196
34307
|
const occurrences = [];
|
|
34197
34308
|
let applicableCount = 0;
|
|
@@ -34545,11 +34656,10 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
34545
34656
|
return {ruleId: rule.ruleId, outcome: 'cantTell', severity: 'minor', occurrences};
|
|
34546
34657
|
}), applicability: null },
|
|
34547
34658
|
"option-name-present": { run: (function runInPage(ctx) {
|
|
34548
|
-
const { document,
|
|
34659
|
+
const { document, helpers, rule } = ctx;
|
|
34549
34660
|
const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
|
|
34550
34661
|
? helpers.getEligibilityInfo
|
|
34551
34662
|
: null;
|
|
34552
|
-
const safeRoot = root || document;
|
|
34553
34663
|
|
|
34554
34664
|
|
|
34555
34665
|
function normalizeWs(s) {
|
|
@@ -34613,7 +34723,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
34613
34723
|
let applicableCount = 0;
|
|
34614
34724
|
|
|
34615
34725
|
const selector = "[role=\"option\"]";
|
|
34616
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
34726
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
34617
34727
|
|
|
34618
34728
|
|
|
34619
34729
|
function hasName(el) {
|
|
@@ -34676,8 +34786,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
34676
34786
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
34677
34787
|
}), applicability: null },
|
|
34678
34788
|
"p-as-heading": { run: (function runInPage(ctx) {
|
|
34679
|
-
const { document,
|
|
34680
|
-
const safeRoot = root || document;
|
|
34789
|
+
const { document, helpers, rule } = ctx;
|
|
34681
34790
|
|
|
34682
34791
|
const MAX_HEADING_LIKE_CHARS = 120;
|
|
34683
34792
|
const MIN_FONT_SIZE_PX = 18;
|
|
@@ -34729,7 +34838,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
34729
34838
|
return Number.isFinite(px) ? px : 0;
|
|
34730
34839
|
}
|
|
34731
34840
|
|
|
34732
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('p'
|
|
34841
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('p') : helpers.queryAll('p');
|
|
34733
34842
|
|
|
34734
34843
|
const occurrences = [];
|
|
34735
34844
|
let applicableCount = 0;
|
|
@@ -34811,14 +34920,31 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
34811
34920
|
return tag === 'h1';
|
|
34812
34921
|
}
|
|
34813
34922
|
|
|
34923
|
+
const isAccTreeEligible = helpers && typeof helpers.isAccTreeEligible === 'function' ? helpers.isAccTreeEligible : null;
|
|
34924
|
+
|
|
34925
|
+
function isExposedToAt(el) {
|
|
34926
|
+
if (!isAccTreeEligible) return true;
|
|
34927
|
+
try {
|
|
34928
|
+
const r = isAccTreeEligible(el, ctx);
|
|
34929
|
+
if (typeof r === 'boolean') return r;
|
|
34930
|
+
return !!(r && r.eligible);
|
|
34931
|
+
} catch {
|
|
34932
|
+
return true;
|
|
34933
|
+
}
|
|
34934
|
+
}
|
|
34935
|
+
|
|
34936
|
+
// queryAllSmart (shadow-DOM-aware) instead of plain document.querySelectorAll -- see
|
|
34937
|
+
// landmark-one-main-manual.js's identical precedent.
|
|
34814
34938
|
let nodes = [];
|
|
34815
34939
|
try {
|
|
34816
|
-
nodes =
|
|
34940
|
+
nodes = helpers && typeof helpers.queryAllSmart === 'function'
|
|
34941
|
+
? helpers.queryAllSmart('h1, [role]')
|
|
34942
|
+
: document.querySelectorAll('h1, [role]');
|
|
34817
34943
|
} catch {
|
|
34818
34944
|
nodes = [];
|
|
34819
34945
|
}
|
|
34820
34946
|
|
|
34821
|
-
const hasH1 = Array.from(nodes).some((el) => el && isLevelOneHeading(el));
|
|
34947
|
+
const hasH1 = Array.from(nodes).some((el) => el && isLevelOneHeading(el) && isExposedToAt(el));
|
|
34822
34948
|
|
|
34823
34949
|
if (hasH1) {
|
|
34824
34950
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
@@ -35142,8 +35268,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
35142
35268
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
35143
35269
|
}), applicability: null },
|
|
35144
35270
|
"presentation-role-conflict": { run: (function runInPage(ctx) {
|
|
35145
|
-
const { document,
|
|
35146
|
-
const safeRoot = root || document;
|
|
35271
|
+
const { document, helpers, rule } = ctx;
|
|
35147
35272
|
|
|
35148
35273
|
// The full set of ARIA attributes marked `global: true` per the WAI-ARIA
|
|
35149
35274
|
// spec (confirmed against a widely-used reference engine's own
|
|
@@ -35160,8 +35285,8 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
35160
35285
|
const getFocusableInfo = helpers && typeof helpers.getFocusableInfo === 'function' ? helpers.getFocusableInfo : null;
|
|
35161
35286
|
|
|
35162
35287
|
const nodes = helpers.queryAllSmart
|
|
35163
|
-
? helpers.queryAllSmart('[role="presentation"], [role="none"], img[alt=""]'
|
|
35164
|
-
: helpers.queryAll('[role="presentation"], [role="none"], img[alt=""]'
|
|
35288
|
+
? helpers.queryAllSmart('[role="presentation"], [role="none"], img[alt=""]')
|
|
35289
|
+
: helpers.queryAll('[role="presentation"], [role="none"], img[alt=""]');
|
|
35165
35290
|
|
|
35166
35291
|
const occurrences = [];
|
|
35167
35292
|
let applicableCount = 0;
|
|
@@ -35225,11 +35350,10 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
35225
35350
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
35226
35351
|
}), applicability: null },
|
|
35227
35352
|
"progressbar-name-present": { run: (function runInPage(ctx) {
|
|
35228
|
-
const { document,
|
|
35353
|
+
const { document, helpers, rule } = ctx;
|
|
35229
35354
|
const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
|
|
35230
35355
|
? helpers.getEligibilityInfo
|
|
35231
35356
|
: null;
|
|
35232
|
-
const safeRoot = root || document;
|
|
35233
35357
|
|
|
35234
35358
|
|
|
35235
35359
|
function normalizeWs(s) {
|
|
@@ -35293,7 +35417,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
35293
35417
|
let applicableCount = 0;
|
|
35294
35418
|
|
|
35295
35419
|
const selector = '[role="progressbar"]';
|
|
35296
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
35420
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
35297
35421
|
|
|
35298
35422
|
|
|
35299
35423
|
function evaluate(el) {
|
|
@@ -35705,8 +35829,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
35705
35829
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
35706
35830
|
}), applicability: null },
|
|
35707
35831
|
"scrollable-region-focusable": { run: (function runInPage(ctx) {
|
|
35708
|
-
const { document,
|
|
35709
|
-
const safeRoot = root || document;
|
|
35832
|
+
const { document, helpers, rule } = ctx;
|
|
35710
35833
|
|
|
35711
35834
|
function safeComputedStyle(el) {
|
|
35712
35835
|
try {
|
|
@@ -35751,7 +35874,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
35751
35874
|
}
|
|
35752
35875
|
|
|
35753
35876
|
const CANDIDATE_SELECTOR = 'div, section, article, aside, main, nav, pre, table, blockquote, ul, ol, textarea';
|
|
35754
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(CANDIDATE_SELECTOR
|
|
35877
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(CANDIDATE_SELECTOR) : helpers.queryAll(CANDIDATE_SELECTOR);
|
|
35755
35878
|
|
|
35756
35879
|
const occurrences = [];
|
|
35757
35880
|
let applicableCount = 0;
|
|
@@ -35806,11 +35929,10 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
35806
35929
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
35807
35930
|
}), applicability: null },
|
|
35808
35931
|
"searchbox-name-present": { run: (function runInPage(ctx) {
|
|
35809
|
-
const { document,
|
|
35932
|
+
const { document, helpers, rule } = ctx;
|
|
35810
35933
|
const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
|
|
35811
35934
|
? helpers.getEligibilityInfo
|
|
35812
35935
|
: null;
|
|
35813
|
-
const safeRoot = root || document;
|
|
35814
35936
|
|
|
35815
35937
|
|
|
35816
35938
|
function normalizeWs(s) {
|
|
@@ -35915,7 +36037,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
35915
36037
|
let applicableCount = 0;
|
|
35916
36038
|
|
|
35917
36039
|
const selector = "[role=\"searchbox\"]";
|
|
35918
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
36040
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
35919
36041
|
|
|
35920
36042
|
// Precompute label[for] map for searchbox elements that are labelable
|
|
35921
36043
|
// native form controls (e.g. <input role="searchbox">).
|
|
@@ -36016,10 +36138,9 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
36016
36138
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
36017
36139
|
}), applicability: null },
|
|
36018
36140
|
"server-side-image-map-absent": { run: (function runInPage(ctx) {
|
|
36019
|
-
const { document,
|
|
36020
|
-
const safeRoot = root || document;
|
|
36141
|
+
const { document, helpers, rule } = ctx;
|
|
36021
36142
|
|
|
36022
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('img[ismap]'
|
|
36143
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('img[ismap]') : helpers.queryAll('img[ismap]');
|
|
36023
36144
|
|
|
36024
36145
|
const occurrences = [];
|
|
36025
36146
|
let applicableCount = 0;
|
|
@@ -36057,8 +36178,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
36057
36178
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
36058
36179
|
}), applicability: null },
|
|
36059
36180
|
"skip-link": { run: (function runInPage(ctx) {
|
|
36060
|
-
const { document,
|
|
36061
|
-
const safeRoot = root || document;
|
|
36181
|
+
const { document, helpers, rule } = ctx;
|
|
36062
36182
|
|
|
36063
36183
|
function normalizeWs(s) {
|
|
36064
36184
|
return String(s || '').replace(/\s+/g, ' ').trim();
|
|
@@ -36085,7 +36205,31 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
36085
36205
|
return normalizeWs(el.textContent);
|
|
36086
36206
|
}
|
|
36087
36207
|
|
|
36088
|
-
|
|
36208
|
+
function hasReliableGeometrySupport() {
|
|
36209
|
+
const probe = document.documentElement || document.body || null;
|
|
36210
|
+
if (!probe || !probe.getClientRects || !probe.getBoundingClientRect) return false;
|
|
36211
|
+
try {
|
|
36212
|
+
const rects = probe.getClientRects();
|
|
36213
|
+
const rectCount = rects ? rects.length : 0;
|
|
36214
|
+
const r = probe.getBoundingClientRect();
|
|
36215
|
+
const w = r && Number.isFinite(r.width) ? r.width : 0;
|
|
36216
|
+
const h = r && Number.isFinite(r.height) ? r.height : 0;
|
|
36217
|
+
return rectCount > 0 && (w > 0 || h > 0);
|
|
36218
|
+
} catch {
|
|
36219
|
+
return false;
|
|
36220
|
+
}
|
|
36221
|
+
}
|
|
36222
|
+
|
|
36223
|
+
function toEligibility(info) {
|
|
36224
|
+
return {
|
|
36225
|
+
eligible: !!(info && info.eligible),
|
|
36226
|
+
reasons: info && Array.isArray(info.reasons) ? info.reasons.slice(0) : []
|
|
36227
|
+
};
|
|
36228
|
+
}
|
|
36229
|
+
|
|
36230
|
+
const geometrySupported = hasReliableGeometrySupport();
|
|
36231
|
+
|
|
36232
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('a[href]') : helpers.queryAll('a[href]');
|
|
36089
36233
|
|
|
36090
36234
|
const occurrences = [];
|
|
36091
36235
|
let applicableCount = 0;
|
|
@@ -36123,7 +36267,63 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
36123
36267
|
}
|
|
36124
36268
|
}
|
|
36125
36269
|
|
|
36126
|
-
if (target)
|
|
36270
|
+
if (target) {
|
|
36271
|
+
const accEligibility = toEligibility(
|
|
36272
|
+
helpers.getEligibilityInfo
|
|
36273
|
+
? helpers.getEligibilityInfo(target, ctx, { targetSet: 'acc' })
|
|
36274
|
+
: (helpers.isAccTreeEligible ? helpers.isAccTreeEligible(target, ctx) : { eligible: true, reasons: [] })
|
|
36275
|
+
);
|
|
36276
|
+
|
|
36277
|
+
let geometryEligibility = null;
|
|
36278
|
+
let geometryReasonCode = null;
|
|
36279
|
+
if (geometrySupported && helpers.isDomVisibleEligible) {
|
|
36280
|
+
geometryEligibility = toEligibility(
|
|
36281
|
+
helpers.isDomVisibleEligible(target, ctx, { visibilityMode: 'styleAndGeometry', ignoreOpacity: true })
|
|
36282
|
+
);
|
|
36283
|
+
if (!geometryEligibility.eligible && geometryEligibility.reasons.includes('noClientRects')) {
|
|
36284
|
+
geometryReasonCode = 'NO_CLIENT_RECTS';
|
|
36285
|
+
} else if (!geometryEligibility.eligible && geometryEligibility.reasons.includes('zeroArea')) {
|
|
36286
|
+
geometryReasonCode = 'ZERO_AREA_TARGET';
|
|
36287
|
+
}
|
|
36288
|
+
}
|
|
36289
|
+
|
|
36290
|
+
const unusableByAcc = !accEligibility.eligible;
|
|
36291
|
+
const unusableByGeometry = !!geometryReasonCode;
|
|
36292
|
+
if (!unusableByAcc && !unusableByGeometry) continue;
|
|
36293
|
+
|
|
36294
|
+
const stableSelector = helpers.buildSelector ? helpers.buildSelector(el) : 'html';
|
|
36295
|
+
const html = helpers.getOuterHtmlSnippet ? helpers.getOuterHtmlSnippet(el) : (el.outerHTML || '');
|
|
36296
|
+
|
|
36297
|
+
occurrences.push({
|
|
36298
|
+
selector: stableSelector,
|
|
36299
|
+
html,
|
|
36300
|
+
summary: 'This skip link points to a target that exists but is not currently usable.',
|
|
36301
|
+
hint: 'Point this skip link to a target that is exposed and usable as a navigation destination.',
|
|
36302
|
+
i18n: {
|
|
36303
|
+
summaryKey: 'skipLink_summary_unusableTarget_cantTell',
|
|
36304
|
+
hintKey: 'skipLink_hint_unusableTarget_cantTell',
|
|
36305
|
+
params: { href }
|
|
36306
|
+
},
|
|
36307
|
+
data: {
|
|
36308
|
+
details: {
|
|
36309
|
+
reasonCode: 'SKIP_LINK_TARGET_UNUSABLE',
|
|
36310
|
+
href,
|
|
36311
|
+
unusableReasonCode: unusableByAcc ? 'ACC_TREE_INELIGIBLE' : geometryReasonCode,
|
|
36312
|
+
targetSelector: helpers.buildSelector ? helpers.buildSelector(target) : null,
|
|
36313
|
+
geometryCheckEnabled: geometrySupported
|
|
36314
|
+
},
|
|
36315
|
+
visibilityFilter: {
|
|
36316
|
+
targetSet: 'acc',
|
|
36317
|
+
accEligible: accEligibility.eligible,
|
|
36318
|
+
reasons: accEligibility.reasons
|
|
36319
|
+
},
|
|
36320
|
+
targetGeometry: geometryEligibility
|
|
36321
|
+
? { eligible: geometryEligibility.eligible, reasons: geometryEligibility.reasons }
|
|
36322
|
+
: { eligible: null, reasons: [] }
|
|
36323
|
+
}
|
|
36324
|
+
});
|
|
36325
|
+
continue;
|
|
36326
|
+
}
|
|
36127
36327
|
|
|
36128
36328
|
const stableSelector = helpers.buildSelector ? helpers.buildSelector(el) : 'html';
|
|
36129
36329
|
const html = helpers.getOuterHtmlSnippet ? helpers.getOuterHtmlSnippet(el) : (el.outerHTML || '');
|
|
@@ -36153,11 +36353,10 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
36153
36353
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
36154
36354
|
}), applicability: null },
|
|
36155
36355
|
"slider-name-present": { run: (function runInPage(ctx) {
|
|
36156
|
-
const { document,
|
|
36356
|
+
const { document, helpers, rule } = ctx;
|
|
36157
36357
|
const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
|
|
36158
36358
|
? helpers.getEligibilityInfo
|
|
36159
36359
|
: null;
|
|
36160
|
-
const safeRoot = root || document;
|
|
36161
36360
|
|
|
36162
36361
|
|
|
36163
36362
|
function normalizeWs(s) {
|
|
@@ -36289,7 +36488,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
36289
36488
|
let applicableCount = 0;
|
|
36290
36489
|
|
|
36291
36490
|
const selector = 'input[type="range"], [role="slider"]';
|
|
36292
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
36491
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
36293
36492
|
|
|
36294
36493
|
|
|
36295
36494
|
// Precompute label[for] map for native range inputs.
|
|
@@ -36403,11 +36602,10 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
36403
36602
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
36404
36603
|
}), applicability: null },
|
|
36405
36604
|
"spinbutton-name-present": { run: (function runInPage(ctx) {
|
|
36406
|
-
const { document,
|
|
36605
|
+
const { document, helpers, rule } = ctx;
|
|
36407
36606
|
const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
|
|
36408
36607
|
? helpers.getEligibilityInfo
|
|
36409
36608
|
: null;
|
|
36410
|
-
const safeRoot = root || document;
|
|
36411
36609
|
|
|
36412
36610
|
|
|
36413
36611
|
function normalizeWs(s) {
|
|
@@ -36512,7 +36710,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
36512
36710
|
let applicableCount = 0;
|
|
36513
36711
|
|
|
36514
36712
|
const selector = "[role=\"spinbutton\"]";
|
|
36515
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
36713
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
36516
36714
|
|
|
36517
36715
|
// Precompute label[for] map for spinbutton elements that are labelable
|
|
36518
36716
|
// native form controls (e.g. <input role="spinbutton">).
|
|
@@ -36613,11 +36811,10 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
36613
36811
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
36614
36812
|
}), applicability: null },
|
|
36615
36813
|
"summary-name-present": { run: (function runInPage(ctx) {
|
|
36616
|
-
const { document,
|
|
36814
|
+
const { document, helpers, rule } = ctx;
|
|
36617
36815
|
const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
|
|
36618
36816
|
? helpers.getEligibilityInfo
|
|
36619
36817
|
: null;
|
|
36620
|
-
const safeRoot = root || document;
|
|
36621
36818
|
|
|
36622
36819
|
|
|
36623
36820
|
function normalizeWs(s) {
|
|
@@ -36680,7 +36877,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
36680
36877
|
const occurrences = [];
|
|
36681
36878
|
let applicableCount = 0;
|
|
36682
36879
|
|
|
36683
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('summary'
|
|
36880
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('summary') : helpers.queryAll('summary');
|
|
36684
36881
|
|
|
36685
36882
|
function evaluate(el) {
|
|
36686
36883
|
const ariaLabel = getAttr(el, 'aria-label');
|
|
@@ -37319,11 +37516,10 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
37319
37516
|
return {ruleId: rule.ruleId, outcome: 'cantTell', severity: 'minor', occurrences};
|
|
37320
37517
|
}), applicability: null },
|
|
37321
37518
|
"tab-name-present": { run: (function runInPage(ctx) {
|
|
37322
|
-
const { document,
|
|
37519
|
+
const { document, helpers, rule } = ctx;
|
|
37323
37520
|
const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
|
|
37324
37521
|
? helpers.getEligibilityInfo
|
|
37325
37522
|
: null;
|
|
37326
|
-
const safeRoot = root || document;
|
|
37327
37523
|
|
|
37328
37524
|
|
|
37329
37525
|
function normalizeWs(s) {
|
|
@@ -37429,7 +37625,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
37429
37625
|
let applicableCount = 0;
|
|
37430
37626
|
|
|
37431
37627
|
const selector = '[role="tab"]';
|
|
37432
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
37628
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
37433
37629
|
|
|
37434
37630
|
|
|
37435
37631
|
function evaluate(el) {
|
|
@@ -37544,14 +37740,13 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
37544
37740
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
37545
37741
|
}), applicability: null },
|
|
37546
37742
|
"table-duplicate-name": { run: (function runInPage(ctx) {
|
|
37547
|
-
const { document,
|
|
37548
|
-
const safeRoot = root || document;
|
|
37743
|
+
const { document, helpers, rule } = ctx;
|
|
37549
37744
|
|
|
37550
37745
|
function normalizeWs(s) {
|
|
37551
37746
|
return String(s || '').replace(/\s+/g, ' ').trim();
|
|
37552
37747
|
}
|
|
37553
37748
|
|
|
37554
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('table[summary]'
|
|
37749
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('table[summary]') : helpers.queryAll('table[summary]');
|
|
37555
37750
|
|
|
37556
37751
|
const occurrences = [];
|
|
37557
37752
|
let applicableCount = 0;
|
|
@@ -37597,12 +37792,11 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
37597
37792
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
37598
37793
|
}), applicability: null },
|
|
37599
37794
|
"table-fake-caption": { run: (function runInPage(ctx) {
|
|
37600
|
-
const { document,
|
|
37601
|
-
const safeRoot = root || document;
|
|
37795
|
+
const { document, helpers, rule } = ctx;
|
|
37602
37796
|
|
|
37603
37797
|
function trim(v) { return (v == null ? '' : String(v)).trim(); }
|
|
37604
37798
|
|
|
37605
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('table'
|
|
37799
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('table') : helpers.queryAll('table');
|
|
37606
37800
|
|
|
37607
37801
|
const occurrences = [];
|
|
37608
37802
|
let applicableCount = 0;
|
|
@@ -37665,10 +37859,9 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
37665
37859
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
37666
37860
|
}), applicability: null },
|
|
37667
37861
|
"table-headers-attr-valid": { run: (function runInPage(ctx) {
|
|
37668
|
-
const { document,
|
|
37669
|
-
const safeRoot = root || document;
|
|
37862
|
+
const { document, helpers, rule } = ctx;
|
|
37670
37863
|
|
|
37671
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('td[headers], th[headers]'
|
|
37864
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('td[headers], th[headers]') : helpers.queryAll('td[headers], th[headers]');
|
|
37672
37865
|
|
|
37673
37866
|
const occurrences = [];
|
|
37674
37867
|
let applicableCount = 0;
|
|
@@ -37742,10 +37935,9 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
37742
37935
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
37743
37936
|
}), applicability: null },
|
|
37744
37937
|
"table-th-has-data-cells": { run: (function runInPage(ctx) {
|
|
37745
|
-
const { document,
|
|
37746
|
-
const safeRoot = root || document;
|
|
37938
|
+
const { document, helpers, rule } = ctx;
|
|
37747
37939
|
|
|
37748
|
-
const tables = helpers.queryAllSmart ? helpers.queryAllSmart('table'
|
|
37940
|
+
const tables = helpers.queryAllSmart ? helpers.queryAllSmart('table') : helpers.queryAll('table');
|
|
37749
37941
|
|
|
37750
37942
|
const occurrences = [];
|
|
37751
37943
|
let applicableCount = 0;
|
|
@@ -38304,14 +38496,13 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
38304
38496
|
return { ruleId: RULE_ID, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
38305
38497
|
}), applicability: null },
|
|
38306
38498
|
"td-has-header": { run: (function runInPage(ctx) {
|
|
38307
|
-
const { document,
|
|
38308
|
-
const safeRoot = root || document;
|
|
38499
|
+
const { document, helpers, rule } = ctx;
|
|
38309
38500
|
|
|
38310
38501
|
const MIN_SIZE = 4;
|
|
38311
38502
|
|
|
38312
38503
|
function trim(v) { return (v == null ? '' : String(v)).trim(); }
|
|
38313
38504
|
|
|
38314
|
-
const tables = helpers.queryAllSmart ? helpers.queryAllSmart('table'
|
|
38505
|
+
const tables = helpers.queryAllSmart ? helpers.queryAllSmart('table') : helpers.queryAll('table');
|
|
38315
38506
|
|
|
38316
38507
|
const occurrences = [];
|
|
38317
38508
|
let applicableCount = 0;
|
|
@@ -38397,11 +38588,10 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
38397
38588
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
38398
38589
|
}), applicability: null },
|
|
38399
38590
|
"textbox-name-present": { run: (function runInPage(ctx) {
|
|
38400
|
-
const { document,
|
|
38591
|
+
const { document, helpers, rule } = ctx;
|
|
38401
38592
|
const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
|
|
38402
38593
|
? helpers.getEligibilityInfo
|
|
38403
38594
|
: null;
|
|
38404
|
-
const safeRoot = root || document;
|
|
38405
38595
|
|
|
38406
38596
|
|
|
38407
38597
|
function normalizeWs(s) {
|
|
@@ -38506,7 +38696,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
38506
38696
|
let applicableCount = 0;
|
|
38507
38697
|
|
|
38508
38698
|
const selector = "[role=\"textbox\"]";
|
|
38509
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
38699
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
38510
38700
|
|
|
38511
38701
|
// Precompute label[for] map for textbox elements that are labelable
|
|
38512
38702
|
// native form controls (e.g. <input role="textbox">).
|
|
@@ -38607,11 +38797,10 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
38607
38797
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
38608
38798
|
}), applicability: null },
|
|
38609
38799
|
"tooltip-name-present": { run: (function runInPage(ctx) {
|
|
38610
|
-
const { document,
|
|
38800
|
+
const { document, helpers, rule } = ctx;
|
|
38611
38801
|
const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
|
|
38612
38802
|
? helpers.getEligibilityInfo
|
|
38613
38803
|
: null;
|
|
38614
|
-
const safeRoot = root || document;
|
|
38615
38804
|
|
|
38616
38805
|
|
|
38617
38806
|
function normalizeWs(s) {
|
|
@@ -38675,7 +38864,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
38675
38864
|
let applicableCount = 0;
|
|
38676
38865
|
|
|
38677
38866
|
const selector = '[role="tooltip"]';
|
|
38678
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
38867
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
38679
38868
|
|
|
38680
38869
|
|
|
38681
38870
|
function evaluate(el) {
|
|
@@ -38739,11 +38928,10 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
38739
38928
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
38740
38929
|
}), applicability: null },
|
|
38741
38930
|
"treeitem-name-present": { run: (function runInPage(ctx) {
|
|
38742
|
-
const { document,
|
|
38931
|
+
const { document, helpers, rule } = ctx;
|
|
38743
38932
|
const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
|
|
38744
38933
|
? helpers.getEligibilityInfo
|
|
38745
38934
|
: null;
|
|
38746
|
-
const safeRoot = root || document;
|
|
38747
38935
|
|
|
38748
38936
|
|
|
38749
38937
|
function normalizeWs(s) {
|
|
@@ -38807,7 +38995,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
38807
38995
|
let applicableCount = 0;
|
|
38808
38996
|
|
|
38809
38997
|
const selector = "[role=\"treeitem\"]";
|
|
38810
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
38998
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
38811
38999
|
|
|
38812
39000
|
|
|
38813
39001
|
function hasName(el) {
|
|
@@ -38870,12 +39058,11 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
38870
39058
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
38871
39059
|
}), applicability: null },
|
|
38872
39060
|
"valid-lang": { run: (function runInPage(ctx) {
|
|
38873
|
-
const { document,
|
|
38874
|
-
const safeRoot = root || document;
|
|
39061
|
+
const { document, helpers, rule } = ctx;
|
|
38875
39062
|
|
|
38876
39063
|
const BCP47_RE = /^[a-zA-Z]{2,3}(-[a-zA-Z0-9]{2,8})*$/;
|
|
38877
39064
|
|
|
38878
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[lang]'
|
|
39065
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[lang]') : helpers.queryAll('[lang]');
|
|
38879
39066
|
|
|
38880
39067
|
const occurrences = [];
|
|
38881
39068
|
let applicableCount = 0;
|
|
@@ -38920,10 +39107,9 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
38920
39107
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
38921
39108
|
}), applicability: null },
|
|
38922
39109
|
"video-caption": { run: (function runInPage(ctx) {
|
|
38923
|
-
const { document,
|
|
38924
|
-
const safeRoot = root || document;
|
|
39110
|
+
const { document, helpers, rule } = ctx;
|
|
38925
39111
|
|
|
38926
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('video'
|
|
39112
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('video') : helpers.queryAll('video');
|
|
38927
39113
|
|
|
38928
39114
|
const occurrences = [];
|
|
38929
39115
|
let applicableCount = 0;
|
|
@@ -39645,10 +39831,12 @@ const I18N = {
|
|
|
39645
39831
|
"region_description": "Checks that direct children of <body> with visible text content are contained within a landmark region.",
|
|
39646
39832
|
"region_summary_cantTell": "This content is not contained within a landmark region.",
|
|
39647
39833
|
"region_hint_cantTell": "Move this content inside a landmark region (main, nav, aside, a labeled section, etc.).",
|
|
39648
|
-
"skipLink_title": "Skip link must have a resolvable target",
|
|
39649
|
-
"skipLink_description": "Checks that a \"skip to ...\" link's href fragment resolves to a real element in the document.",
|
|
39834
|
+
"skipLink_title": "Skip link must have a resolvable, usable target",
|
|
39835
|
+
"skipLink_description": "Checks that a \"skip to ...\" link's href fragment resolves to a real, currently usable element in the document.",
|
|
39650
39836
|
"skipLink_summary_cantTell": "This skip link's target does not exist.",
|
|
39651
39837
|
"skipLink_hint_cantTell": "Point the skip link's href at an id that exists in the document, or add the missing target element.",
|
|
39838
|
+
"skipLink_summary_unusableTarget_cantTell": "This skip link points to a target that exists but is not currently usable.",
|
|
39839
|
+
"skipLink_hint_unusableTarget_cantTell": "Point this skip link to a target that is exposed and usable as a navigation destination.",
|
|
39652
39840
|
"autocompleteValid_title": "autocomplete attribute must be a valid autofill value",
|
|
39653
39841
|
"autocompleteValid_description": "Checks that a non-empty autocomplete attribute is \"on\"/\"off\" or a well-formed autofill detail token list.",
|
|
39654
39842
|
"autocompleteValid_summary_fail": "This autocomplete attribute value is not a valid autofill value.",
|
|
@@ -40247,10 +40435,12 @@ const I18N = {
|
|
|
40247
40435
|
"region_description": "Vérifie que les enfants directs de <body> ayant un contenu textuel visible sont contenus dans un point de repère.",
|
|
40248
40436
|
"region_summary_cantTell": "Ce contenu n’est contenu dans aucun point de repère.",
|
|
40249
40437
|
"region_hint_cantTell": "Déplacez ce contenu à l’intérieur d’un point de repère (main, nav, aside, une section nommée, etc.).",
|
|
40250
|
-
"skipLink_title": "Un lien d’évitement doit avoir une cible qui se résout",
|
|
40251
|
-
"skipLink_description": "Vérifie que le fragment href d’un lien « aller au... » se résout vers un élément réel du document.",
|
|
40438
|
+
"skipLink_title": "Un lien d’évitement doit avoir une cible qui se résout et qui est utilisable",
|
|
40439
|
+
"skipLink_description": "Vérifie que le fragment href d’un lien « aller au... » se résout vers un élément réel et actuellement utilisable du document.",
|
|
40252
40440
|
"skipLink_summary_cantTell": "La cible de ce lien d’évitement n’existe pas.",
|
|
40253
40441
|
"skipLink_hint_cantTell": "Faites pointer le href du lien d’évitement vers un id qui existe dans le document, ou ajoutez l’élément cible manquant.",
|
|
40442
|
+
"skipLink_summary_unusableTarget_cantTell": "Ce lien d’évitement pointe vers une cible qui existe mais qui n’est pas actuellement utilisable.",
|
|
40443
|
+
"skipLink_hint_unusableTarget_cantTell": "Faites pointer ce lien d’évitement vers une cible exposée et utilisable comme destination de navigation.",
|
|
40254
40444
|
"autocompleteValid_title": "L’attribut autocomplete doit être une valeur d’auto-remplissage valide",
|
|
40255
40445
|
"autocompleteValid_description": "Vérifie qu’un attribut autocomplete non vide vaut « on »/« off » ou une liste de jetons d’auto-remplissage bien formée.",
|
|
40256
40446
|
"autocompleteValid_summary_fail": "Cette valeur d’attribut autocomplete n’est pas une valeur d’auto-remplissage valide.",
|
|
@@ -43056,6 +43246,10 @@ const createDomHelpers = (function createDomHelpers(opts) {
|
|
|
43056
43246
|
})();
|
|
43057
43247
|
// Default on: opt OUT with `includeShadowDom: false`, not opt in.
|
|
43058
43248
|
const includeShadowDom = !(opts && opts.includeShadowDom === false);
|
|
43249
|
+
// Default off: by default, helper queries skip structurally/CSS-hidden
|
|
43250
|
+
// subtrees (display:none, [hidden], closed <details>, etc.). Callers can
|
|
43251
|
+
// opt out with includeHiddenElements:true.
|
|
43252
|
+
const includeHiddenElements = !!(opts && opts.includeHiddenElements === true);
|
|
43059
43253
|
const excludeSelectors = Array.isArray(opts && opts.excludeSelectors) ? opts.excludeSelectors : [];
|
|
43060
43254
|
|
|
43061
43255
|
// Rule-scoped excludes (engineOptions.rules[ruleId].excludeSelectors), set
|
|
@@ -44022,8 +44216,55 @@ const createDomHelpers = (function createDomHelpers(opts) {
|
|
|
44022
44216
|
return results;
|
|
44023
44217
|
}
|
|
44024
44218
|
|
|
44219
|
+
const HARD_HIDDEN_REASONS = new Set([
|
|
44220
|
+
'displayNone',
|
|
44221
|
+
'hiddenAttr',
|
|
44222
|
+
'detailsClosed',
|
|
44223
|
+
'templateContent',
|
|
44224
|
+
'nonRenderedElement',
|
|
44225
|
+
'inputHidden',
|
|
44226
|
+
'visibilityHidden',
|
|
44227
|
+
'contentVisibilityHidden'
|
|
44228
|
+
]);
|
|
44229
|
+
|
|
44025
44230
|
function queryAllSmart(sel) {
|
|
44026
|
-
|
|
44231
|
+
let list = includeShadowDom ? queryAllDeep(sel) : queryAll(sel);
|
|
44232
|
+
|
|
44233
|
+
// Global hidden-content policy: skip nodes that are fully excluded from
|
|
44234
|
+
// rendered visibility by default (unless includeHiddenElements:true).
|
|
44235
|
+
if (!includeHiddenElements) {
|
|
44236
|
+
list = list.filter((el) => {
|
|
44237
|
+
try {
|
|
44238
|
+
const vis = isAccTreeEligible(el);
|
|
44239
|
+
if (!vis || vis.eligible !== false) return true;
|
|
44240
|
+
const reasons = Array.isArray(vis.reasons) ? vis.reasons : [];
|
|
44241
|
+
for (const r of reasons) {
|
|
44242
|
+
if (HARD_HIDDEN_REASONS.has(r)) return false;
|
|
44243
|
+
}
|
|
44244
|
+
|
|
44245
|
+
// `isAccTreeEligible` can short-circuit on an inert ancestor
|
|
44246
|
+
// before it reaches an outer hard-hidden ancestor (e.g.
|
|
44247
|
+
// display:none wrapper). In that case the node is still
|
|
44248
|
+
// structurally hidden and should be excluded by the default
|
|
44249
|
+
// hidden-content policy.
|
|
44250
|
+
if (reasons.includes('inert')) {
|
|
44251
|
+
const domVis = isDomVisibleEligible(el, null, {
|
|
44252
|
+
visibilityMode: 'styleOnly',
|
|
44253
|
+
disableGeometry: true,
|
|
44254
|
+
ignoreOpacity: true
|
|
44255
|
+
});
|
|
44256
|
+
const domReasons = Array.isArray(domVis && domVis.reasons) ? domVis.reasons : [];
|
|
44257
|
+
for (const r of domReasons) {
|
|
44258
|
+
if (HARD_HIDDEN_REASONS.has(r)) return false;
|
|
44259
|
+
}
|
|
44260
|
+
}
|
|
44261
|
+
return true;
|
|
44262
|
+
} catch {
|
|
44263
|
+
return true;
|
|
44264
|
+
}
|
|
44265
|
+
});
|
|
44266
|
+
}
|
|
44267
|
+
|
|
44027
44268
|
return __getEffectiveExcludeSelectors().length ? list.filter((el) => !isExcluded(el)) : list;
|
|
44028
44269
|
}
|
|
44029
44270
|
|
|
@@ -46593,6 +46834,36 @@ const createDomHelpers = (function createDomHelpers(opts) {
|
|
|
46593
46834
|
let node = el;
|
|
46594
46835
|
let safety = 0;
|
|
46595
46836
|
|
|
46837
|
+
// Only apply the "stop climbing once we reach a contextSelector-
|
|
46838
|
+
// matched root" shortcut when there's a single (or no) matched
|
|
46839
|
+
// root -- resolveContextRoots() falls back to `[documentElement]`
|
|
46840
|
+
// when no contextSelector is given, so this is the overwhelmingly
|
|
46841
|
+
// common case and behaves exactly as before.
|
|
46842
|
+
//
|
|
46843
|
+
// With MULTIPLE matched roots (multi-region contextSelector
|
|
46844
|
+
// scans), stopping there without recording anything about which
|
|
46845
|
+
// root produced an ambiguous, non-unique selector string for two
|
|
46846
|
+
// structurally-identical regions -- a real, confirmed bug (found
|
|
46847
|
+
// 2026-07-29 via the cross-engine comparisons project): two
|
|
46848
|
+
// wrapper <div>s, each containing two identical ".widget"
|
|
46849
|
+
// sections scanned via `contextSelector: '.widget'`, produced the
|
|
46850
|
+
// *same* selector string ("section:nth-of-type(1) > div > div >
|
|
46851
|
+
// button") for the equivalent button in each wrapper --
|
|
46852
|
+
// resolving to 2 elements instead of 1 when queried, and pointing
|
|
46853
|
+
// at the wrong one for at least one of the two occurrences. The
|
|
46854
|
+
// existing `el.matches(candidate)` safety check below couldn't
|
|
46855
|
+
// catch this: it only verifies THIS element matches the string,
|
|
46856
|
+
// never that the string is unique document-wide.
|
|
46857
|
+
//
|
|
46858
|
+
// Fix: when multiple roots are in play, don't stop early --
|
|
46859
|
+
// keep climbing (same as the always-correct no-contextSelector
|
|
46860
|
+
// path) until finding a genuinely unique anchor or reaching the
|
|
46861
|
+
// true document root, which is always singular. That restores
|
|
46862
|
+
// the invariant the final safety-check comment below relies on,
|
|
46863
|
+
// rather than needing a separate (more expensive) document-wide
|
|
46864
|
+
// uniqueness re-check.
|
|
46865
|
+
const stopAtMatchedRoot = roots.length <= 1;
|
|
46866
|
+
|
|
46596
46867
|
while (node && node.nodeType === 1 && safety++ < 20) {
|
|
46597
46868
|
let anchor = null;
|
|
46598
46869
|
|
|
@@ -46632,7 +46903,7 @@ const createDomHelpers = (function createDomHelpers(opts) {
|
|
|
46632
46903
|
parts.unshift(nthOfType(node));
|
|
46633
46904
|
}
|
|
46634
46905
|
|
|
46635
|
-
if (!node.parentElement || roots.includes(node)) break;
|
|
46906
|
+
if (!node.parentElement || (stopAtMatchedRoot && roots.includes(node))) break;
|
|
46636
46907
|
node = node.parentElement;
|
|
46637
46908
|
}
|
|
46638
46909
|
|
|
@@ -46651,7 +46922,12 @@ const createDomHelpers = (function createDomHelpers(opts) {
|
|
|
46651
46922
|
// position relative to its own parent via `>` (child, not
|
|
46652
46923
|
// descendant) combinators, so a correctly-matching chain can
|
|
46653
46924
|
// only resolve to one element short of a malformed document
|
|
46654
|
-
// (e.g. two <html> roots)
|
|
46925
|
+
// (e.g. two <html> roots) -- true as long as the walk above
|
|
46926
|
+
// never stops short of a genuinely unique anchor/root, which is
|
|
46927
|
+
// exactly what `stopAtMatchedRoot` now guarantees (see its own
|
|
46928
|
+
// comment above; a multi-root contextSelector scan stopping
|
|
46929
|
+
// early used to violate this invariant silently). Re-deriving
|
|
46930
|
+
// that guarantee via a
|
|
46655
46931
|
// document-wide :nth-of-type scan was measured to cost O(total
|
|
46656
46932
|
// same-tag siblings) per call — pathological on pages with many
|
|
46657
46933
|
// flat, unidentified siblings (e.g. hundreds of unlabeled
|
|
@@ -47218,6 +47494,9 @@ const runCore = (function runCore(pageUrl, contextSelector, engineOptions, runOn
|
|
|
47218
47494
|
|
|
47219
47495
|
// Default on: opt OUT with `includeShadowDom: false`, not opt in.
|
|
47220
47496
|
const includeShadowDom = !(engineOptionsResolved && engineOptionsResolved.includeShadowDom === false);
|
|
47497
|
+
// Default off: hidden/collapsed content is excluded from rule evaluation
|
|
47498
|
+
// unless the caller explicitly opts in.
|
|
47499
|
+
const includeHiddenElements = !!(engineOptionsResolved && engineOptionsResolved.includeHiddenElements === true);
|
|
47221
47500
|
const excludeSelectors = normalizeSelectorList(engineOptionsResolved && engineOptionsResolved.excludeSelectors);
|
|
47222
47501
|
|
|
47223
47502
|
const url = pageUrl || (document.location && document.location.href) || null;
|
|
@@ -47228,11 +47507,27 @@ const runCore = (function runCore(pageUrl, contextSelector, engineOptions, runOn
|
|
|
47228
47507
|
? engineOptionsResolved.timestamp.trim()
|
|
47229
47508
|
: null;
|
|
47230
47509
|
|
|
47510
|
+
// createDomHelpers()/createContrastHelpers() persist their element-keyed
|
|
47511
|
+
// caches (outerHtmlCache, selectorCache, etc.) on window.__a11ycoreSharedCache
|
|
47512
|
+
// so multiple helper instances created *within this run* can share them
|
|
47513
|
+
// deterministically. But a window/document is frequently reused across
|
|
47514
|
+
// SEPARATE runs -- e.g. Jest's jsdom environment creates one window per
|
|
47515
|
+
// test file, and mutating document.body between it() blocks is standard.
|
|
47516
|
+
// Those caches are keyed by element reference, not content, so a run that
|
|
47517
|
+
// reuses an already-cached element (document.body never changes identity)
|
|
47518
|
+
// would otherwise read stale data cached by an earlier, unrelated run on
|
|
47519
|
+
// the same window. Clearing at the start of every run keeps sharing scoped
|
|
47520
|
+
// to "this run" as intended, without leaking across runs.
|
|
47521
|
+
try {
|
|
47522
|
+
if (window && window.__a11ycoreSharedCache) window.__a11ycoreSharedCache = {};
|
|
47523
|
+
} catch {}
|
|
47524
|
+
|
|
47231
47525
|
const sharedHelpers = createDomHelpers({
|
|
47232
47526
|
document,
|
|
47233
47527
|
window,
|
|
47234
47528
|
root: roots,
|
|
47235
47529
|
includeShadowDom,
|
|
47530
|
+
includeHiddenElements,
|
|
47236
47531
|
excludeSelectors,
|
|
47237
47532
|
// Optional perf counters (bench/debug only). Deterministic and per-run.
|
|
47238
47533
|
perfStats: !!(engineOptionsResolved && engineOptionsResolved.perfStats)
|
|
@@ -53296,8 +53591,8 @@ const __a11yCoreCrossFrameApi = (function () {
|
|
|
53296
53591
|
},
|
|
53297
53592
|
{
|
|
53298
53593
|
"ruleId": "skip-link",
|
|
53299
|
-
"title": "Skip link must have a resolvable target",
|
|
53300
|
-
"description": "Checks that a \"skip to ...\" link's href fragment resolves to a real element in the document.",
|
|
53594
|
+
"title": "Skip link must have a resolvable, usable target",
|
|
53595
|
+
"description": "Checks that a \"skip to ...\" link's href fragment resolves to a real, currently usable element in the document.",
|
|
53301
53596
|
"i18n": {
|
|
53302
53597
|
"titleKey": "skipLink_title",
|
|
53303
53598
|
"descriptionKey": "skipLink_description"
|
|
@@ -54884,13 +55179,7 @@ const __a11yCoreCrossFrameApi = (function () {
|
|
|
54884
55179
|
const RULE_IMPLS = {
|
|
54885
55180
|
"accesskeys": { run: (function runInPage(ctx) {
|
|
54886
55181
|
const { document, helpers, rule } = ctx;
|
|
54887
|
-
|
|
54888
|
-
let nodes = [];
|
|
54889
|
-
try {
|
|
54890
|
-
nodes = document.querySelectorAll('[accesskey]');
|
|
54891
|
-
} catch {
|
|
54892
|
-
nodes = [];
|
|
54893
|
-
}
|
|
55182
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[accesskey]') : helpers.queryAll('[accesskey]');
|
|
54894
55183
|
|
|
54895
55184
|
const groups = new Map(); // normalized key -> elements[]
|
|
54896
55185
|
for (const el of nodes) {
|
|
@@ -55481,8 +55770,7 @@ if (isAccTreeEligible) {
|
|
|
55481
55770
|
return { ruleId: rule.ruleId, outcome: 'cantTell', severity: 'minor', occurrences };
|
|
55482
55771
|
}), applicability: null },
|
|
55483
55772
|
"aria-allowed-attr": { run: (function runInPage(ctx) {
|
|
55484
|
-
const { document,
|
|
55485
|
-
const safeRoot = root || document;
|
|
55773
|
+
const { document, helpers, rule } = ctx;
|
|
55486
55774
|
|
|
55487
55775
|
const ariaHelpers = helpers && helpers.aria ? helpers.aria : null;
|
|
55488
55776
|
if (!ariaHelpers) {
|
|
@@ -55546,7 +55834,7 @@ if (isAccTreeEligible) {
|
|
|
55546
55834
|
};
|
|
55547
55835
|
|
|
55548
55836
|
const globalSet = new Set(GLOBAL_ATTRS);
|
|
55549
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role]'
|
|
55837
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role]') : helpers.queryAll('[role]');
|
|
55550
55838
|
|
|
55551
55839
|
const occurrences = [];
|
|
55552
55840
|
let applicableCount = 0;
|
|
@@ -55611,15 +55899,14 @@ if (isAccTreeEligible) {
|
|
|
55611
55899
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
55612
55900
|
}), applicability: null },
|
|
55613
55901
|
"aria-allowed-role": { run: (function runInPage(ctx) {
|
|
55614
|
-
const { document,
|
|
55615
|
-
const safeRoot = root || document;
|
|
55902
|
+
const { document, helpers, rule } = ctx;
|
|
55616
55903
|
|
|
55617
55904
|
const ariaHelpers = helpers && helpers.aria ? helpers.aria : null;
|
|
55618
55905
|
if (!ariaHelpers) {
|
|
55619
55906
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
55620
55907
|
}
|
|
55621
55908
|
|
|
55622
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role]'
|
|
55909
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role]') : helpers.queryAll('[role]');
|
|
55623
55910
|
|
|
55624
55911
|
const occurrences = [];
|
|
55625
55912
|
let applicableCount = 0;
|
|
@@ -55666,8 +55953,7 @@ if (isAccTreeEligible) {
|
|
|
55666
55953
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
55667
55954
|
}), applicability: null },
|
|
55668
55955
|
"aria-braille-equivalent": { run: (function runInPage(ctx) {
|
|
55669
|
-
const { document,
|
|
55670
|
-
const safeRoot = root || document;
|
|
55956
|
+
const { document, helpers, rule } = ctx;
|
|
55671
55957
|
|
|
55672
55958
|
function trim(v) { return (v == null ? '' : String(v)).trim(); }
|
|
55673
55959
|
|
|
@@ -55688,8 +55974,8 @@ if (isAccTreeEligible) {
|
|
|
55688
55974
|
}
|
|
55689
55975
|
|
|
55690
55976
|
const nodes = helpers.queryAllSmart
|
|
55691
|
-
? helpers.queryAllSmart('[aria-braillelabel], [aria-brailleroledescription]'
|
|
55692
|
-
: helpers.queryAll('[aria-braillelabel], [aria-brailleroledescription]'
|
|
55977
|
+
? helpers.queryAllSmart('[aria-braillelabel], [aria-brailleroledescription]')
|
|
55978
|
+
: helpers.queryAll('[aria-braillelabel], [aria-brailleroledescription]');
|
|
55693
55979
|
|
|
55694
55980
|
const occurrences = [];
|
|
55695
55981
|
let applicableCount = 0;
|
|
@@ -55750,14 +56036,13 @@ if (isAccTreeEligible) {
|
|
|
55750
56036
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
55751
56037
|
}), applicability: null },
|
|
55752
56038
|
"aria-checked-state-mismatch": { run: (function runInPage(ctx) {
|
|
55753
|
-
const { document,
|
|
55754
|
-
const safeRoot = root || document;
|
|
56039
|
+
const { document, helpers, rule } = ctx;
|
|
55755
56040
|
|
|
55756
56041
|
function trim(v) { return (v == null ? '' : String(v)).trim(); }
|
|
55757
56042
|
|
|
55758
56043
|
const nodes = helpers.queryAllSmart
|
|
55759
|
-
? helpers.queryAllSmart('input[type="checkbox"][aria-checked], input[type="radio"][aria-checked]'
|
|
55760
|
-
: helpers.queryAll('input[type="checkbox"][aria-checked], input[type="radio"][aria-checked]'
|
|
56044
|
+
? helpers.queryAllSmart('input[type="checkbox"][aria-checked], input[type="radio"][aria-checked]')
|
|
56045
|
+
: helpers.queryAll('input[type="checkbox"][aria-checked], input[type="radio"][aria-checked]');
|
|
55761
56046
|
|
|
55762
56047
|
const occurrences = [];
|
|
55763
56048
|
let applicableCount = 0;
|
|
@@ -55819,16 +56104,15 @@ if (isAccTreeEligible) {
|
|
|
55819
56104
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
55820
56105
|
}), applicability: null },
|
|
55821
56106
|
"aria-conditional-attr": { run: (function runInPage(ctx) {
|
|
55822
|
-
const { document,
|
|
55823
|
-
const safeRoot = root || document;
|
|
56107
|
+
const { document, helpers, rule } = ctx;
|
|
55824
56108
|
|
|
55825
56109
|
function trim(v) { return (v == null ? '' : String(v)).trim(); }
|
|
55826
56110
|
|
|
55827
56111
|
const TRUTHY_INVALID_VALUES = new Set(['true', 'grammar', 'spelling']);
|
|
55828
56112
|
|
|
55829
56113
|
const nodes = helpers.queryAllSmart
|
|
55830
|
-
? helpers.queryAllSmart('[aria-errormessage]'
|
|
55831
|
-
: helpers.queryAll('[aria-errormessage]'
|
|
56114
|
+
? helpers.queryAllSmart('[aria-errormessage]')
|
|
56115
|
+
: helpers.queryAll('[aria-errormessage]');
|
|
55832
56116
|
|
|
55833
56117
|
const occurrences = [];
|
|
55834
56118
|
let applicableCount = 0;
|
|
@@ -55873,15 +56157,14 @@ if (isAccTreeEligible) {
|
|
|
55873
56157
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
55874
56158
|
}), applicability: null },
|
|
55875
56159
|
"aria-deprecated-role": { run: (function runInPage(ctx) {
|
|
55876
|
-
const { document,
|
|
55877
|
-
const safeRoot = root || document;
|
|
56160
|
+
const { document, helpers, rule } = ctx;
|
|
55878
56161
|
|
|
55879
56162
|
const ariaHelpers = helpers && helpers.aria ? helpers.aria : null;
|
|
55880
56163
|
if (!ariaHelpers) {
|
|
55881
56164
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
55882
56165
|
}
|
|
55883
56166
|
|
|
55884
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role]'
|
|
56167
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role]') : helpers.queryAll('[role]');
|
|
55885
56168
|
|
|
55886
56169
|
const occurrences = [];
|
|
55887
56170
|
let applicableCount = 0;
|
|
@@ -56393,8 +56676,7 @@ if (isAccTreeEligible) {
|
|
|
56393
56676
|
return { ruleId: rule.ruleId, outcome: 'fail', severity: rule.defaultSeverity || 'minor', occurrences };
|
|
56394
56677
|
}), applicability: null },
|
|
56395
56678
|
"aria-prohibited-attr": { run: (function runInPage(ctx) {
|
|
56396
|
-
const { document,
|
|
56397
|
-
const safeRoot = root || document;
|
|
56679
|
+
const { document, helpers, rule } = ctx;
|
|
56398
56680
|
|
|
56399
56681
|
const ariaHelpers = helpers && helpers.aria ? helpers.aria : null;
|
|
56400
56682
|
if (!ariaHelpers) {
|
|
@@ -56414,7 +56696,7 @@ if (isAccTreeEligible) {
|
|
|
56414
56696
|
|
|
56415
56697
|
const PROHIBITED_NAMING_ATTRS = ['aria-label', 'aria-labelledby'];
|
|
56416
56698
|
|
|
56417
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role]'
|
|
56699
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role]') : helpers.queryAll('[role]');
|
|
56418
56700
|
|
|
56419
56701
|
const occurrences = [];
|
|
56420
56702
|
let applicableCount = 0;
|
|
@@ -56465,8 +56747,7 @@ if (isAccTreeEligible) {
|
|
|
56465
56747
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
56466
56748
|
}), applicability: null },
|
|
56467
56749
|
"aria-prohibited-children": { run: (function runInPage(ctx) {
|
|
56468
|
-
const { document,
|
|
56469
|
-
const safeRoot = root || document;
|
|
56750
|
+
const { document, helpers, rule } = ctx;
|
|
56470
56751
|
|
|
56471
56752
|
const ariaHelpers = helpers && helpers.aria ? helpers.aria : null;
|
|
56472
56753
|
if (!ariaHelpers) {
|
|
@@ -56555,7 +56836,7 @@ if (isAccTreeEligible) {
|
|
|
56555
56836
|
}
|
|
56556
56837
|
}
|
|
56557
56838
|
|
|
56558
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role]'
|
|
56839
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role]') : helpers.queryAll('[role]');
|
|
56559
56840
|
|
|
56560
56841
|
const occurrences = [];
|
|
56561
56842
|
let applicableCount = 0;
|
|
@@ -56627,8 +56908,7 @@ if (isAccTreeEligible) {
|
|
|
56627
56908
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
56628
56909
|
}), applicability: null },
|
|
56629
56910
|
"aria-required-attr": { run: (function runInPage(ctx) {
|
|
56630
|
-
const { document,
|
|
56631
|
-
const safeRoot = root || document;
|
|
56911
|
+
const { document, helpers, rule } = ctx;
|
|
56632
56912
|
|
|
56633
56913
|
const ariaHelpers = helpers && helpers.aria ? helpers.aria : null;
|
|
56634
56914
|
if (!ariaHelpers) {
|
|
@@ -56652,7 +56932,7 @@ if (isAccTreeEligible) {
|
|
|
56652
56932
|
return v != null && String(v).trim().toLowerCase() === 'true';
|
|
56653
56933
|
}
|
|
56654
56934
|
|
|
56655
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role]'
|
|
56935
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role]') : helpers.queryAll('[role]');
|
|
56656
56936
|
|
|
56657
56937
|
const occurrences = [];
|
|
56658
56938
|
let applicableCount = 0;
|
|
@@ -56709,8 +56989,7 @@ if (isAccTreeEligible) {
|
|
|
56709
56989
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
56710
56990
|
}), applicability: null },
|
|
56711
56991
|
"aria-required-children": { run: (function runInPage(ctx) {
|
|
56712
|
-
const { document,
|
|
56713
|
-
const safeRoot = root || document;
|
|
56992
|
+
const { document, helpers, rule } = ctx;
|
|
56714
56993
|
|
|
56715
56994
|
const ariaHelpers = helpers && helpers.aria ? helpers.aria : null;
|
|
56716
56995
|
if (!ariaHelpers) {
|
|
@@ -56741,7 +57020,7 @@ if (isAccTreeEligible) {
|
|
|
56741
57020
|
// ("runInPage MUST be self-contained").
|
|
56742
57021
|
const CANDIDATE_SELECTOR = '[role], li, option, tr, td, th, thead, tbody, tfoot, ul, ol, table, select, input[type="radio"]';
|
|
56743
57022
|
|
|
56744
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role]'
|
|
57023
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role]') : helpers.queryAll('[role]');
|
|
56745
57024
|
|
|
56746
57025
|
const occurrences = [];
|
|
56747
57026
|
let applicableCount = 0;
|
|
@@ -56900,8 +57179,7 @@ if (isAccTreeEligible) {
|
|
|
56900
57179
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
56901
57180
|
}), applicability: null },
|
|
56902
57181
|
"aria-required-parent": { run: (function runInPage(ctx) {
|
|
56903
|
-
const { document,
|
|
56904
|
-
const safeRoot = root || document;
|
|
57182
|
+
const { document, helpers, rule } = ctx;
|
|
56905
57183
|
|
|
56906
57184
|
const ariaHelpers = helpers && helpers.aria ? helpers.aria : null;
|
|
56907
57185
|
if (!ariaHelpers) {
|
|
@@ -56987,7 +57265,7 @@ if (isAccTreeEligible) {
|
|
|
56987
57265
|
const idTok = elId && String(elId).trim();
|
|
56988
57266
|
if (!idTok) return false;
|
|
56989
57267
|
|
|
56990
|
-
const owners = helpers.queryAllSmart ? helpers.queryAllSmart('[aria-owns]'
|
|
57268
|
+
const owners = helpers.queryAllSmart ? helpers.queryAllSmart('[aria-owns]') : helpers.queryAll('[aria-owns]');
|
|
56991
57269
|
for (const owner of owners) {
|
|
56992
57270
|
if (!owner || !owner.getAttribute) continue;
|
|
56993
57271
|
const ownsAttr = owner.getAttribute('aria-owns') || '';
|
|
@@ -57000,7 +57278,7 @@ if (isAccTreeEligible) {
|
|
|
57000
57278
|
return false;
|
|
57001
57279
|
}
|
|
57002
57280
|
|
|
57003
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role]'
|
|
57281
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role]') : helpers.queryAll('[role]');
|
|
57004
57282
|
|
|
57005
57283
|
const occurrences = [];
|
|
57006
57284
|
let applicableCount = 0;
|
|
@@ -57213,15 +57491,14 @@ if (isAccTreeEligible) {
|
|
|
57213
57491
|
return { ruleId: rule.ruleId, outcome: 'fail', severity: rule.defaultSeverity || 'minor', occurrences };
|
|
57214
57492
|
}), applicability: null },
|
|
57215
57493
|
"aria-roles-valid": { run: (function runInPage(ctx) {
|
|
57216
|
-
const { document,
|
|
57217
|
-
const safeRoot = root || document;
|
|
57494
|
+
const { document, helpers, rule } = ctx;
|
|
57218
57495
|
|
|
57219
57496
|
const ariaHelpers = helpers && helpers.aria ? helpers.aria : null;
|
|
57220
57497
|
if (!ariaHelpers) {
|
|
57221
57498
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
57222
57499
|
}
|
|
57223
57500
|
|
|
57224
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role]'
|
|
57501
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role]') : helpers.queryAll('[role]');
|
|
57225
57502
|
|
|
57226
57503
|
const occurrences = [];
|
|
57227
57504
|
let applicableCount = 0;
|
|
@@ -57276,13 +57553,12 @@ if (isAccTreeEligible) {
|
|
|
57276
57553
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
57277
57554
|
}), applicability: null },
|
|
57278
57555
|
"aria-text": { run: (function runInPage(ctx) {
|
|
57279
|
-
const { document,
|
|
57280
|
-
const safeRoot = root || document;
|
|
57556
|
+
const { document, helpers, rule } = ctx;
|
|
57281
57557
|
|
|
57282
57558
|
const FOCUSABLE_DESCENDANT_SELECTOR =
|
|
57283
57559
|
'a[href], button, input, select, textarea, [tabindex], iframe, [contenteditable]:not([contenteditable="false"])';
|
|
57284
57560
|
|
|
57285
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role="text"]'
|
|
57561
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role="text"]') : helpers.queryAll('[role="text"]');
|
|
57286
57562
|
|
|
57287
57563
|
const occurrences = [];
|
|
57288
57564
|
let applicableCount = 0;
|
|
@@ -57336,15 +57612,14 @@ if (isAccTreeEligible) {
|
|
|
57336
57612
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
57337
57613
|
}), applicability: null },
|
|
57338
57614
|
"aria-valid-attr": { run: (function runInPage(ctx) {
|
|
57339
|
-
const { document,
|
|
57340
|
-
const safeRoot = root || document;
|
|
57615
|
+
const { document, helpers, rule } = ctx;
|
|
57341
57616
|
|
|
57342
57617
|
const ariaHelpers = helpers && helpers.aria ? helpers.aria : null;
|
|
57343
57618
|
if (!ariaHelpers) {
|
|
57344
57619
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
57345
57620
|
}
|
|
57346
57621
|
|
|
57347
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('*'
|
|
57622
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('*') : helpers.queryAll('*');
|
|
57348
57623
|
|
|
57349
57624
|
const occurrences = [];
|
|
57350
57625
|
let applicableCount = 0;
|
|
@@ -57398,15 +57673,14 @@ if (isAccTreeEligible) {
|
|
|
57398
57673
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
57399
57674
|
}), applicability: null },
|
|
57400
57675
|
"aria-valid-attr-value": { run: (function runInPage(ctx) {
|
|
57401
|
-
const { document,
|
|
57402
|
-
const safeRoot = root || document;
|
|
57676
|
+
const { document, helpers, rule } = ctx;
|
|
57403
57677
|
|
|
57404
57678
|
const ariaHelpers = helpers && helpers.aria ? helpers.aria : null;
|
|
57405
57679
|
if (!ariaHelpers) {
|
|
57406
57680
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
57407
57681
|
}
|
|
57408
57682
|
|
|
57409
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('*'
|
|
57683
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('*') : helpers.queryAll('*');
|
|
57410
57684
|
|
|
57411
57685
|
const occurrences = [];
|
|
57412
57686
|
let applicableCount = 0;
|
|
@@ -57463,8 +57737,7 @@ if (isAccTreeEligible) {
|
|
|
57463
57737
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
57464
57738
|
}), applicability: null },
|
|
57465
57739
|
"autocomplete-valid": { run: (function runInPage(ctx) {
|
|
57466
|
-
const { document,
|
|
57467
|
-
const safeRoot = root || document;
|
|
57740
|
+
const { document, helpers, rule } = ctx;
|
|
57468
57741
|
|
|
57469
57742
|
// Declared inside runInPage — see scripts/build-core.js header
|
|
57470
57743
|
// ("runInPage MUST be self-contained").
|
|
@@ -57499,7 +57772,7 @@ if (isAccTreeEligible) {
|
|
|
57499
57772
|
return FIELD_NAMES.has(remaining[0]);
|
|
57500
57773
|
}
|
|
57501
57774
|
|
|
57502
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('input, select, textarea'
|
|
57775
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('input, select, textarea') : helpers.queryAll('input, select, textarea');
|
|
57503
57776
|
|
|
57504
57777
|
const occurrences = [];
|
|
57505
57778
|
let applicableCount = 0;
|
|
@@ -57542,12 +57815,11 @@ if (isAccTreeEligible) {
|
|
|
57542
57815
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
57543
57816
|
}), applicability: null },
|
|
57544
57817
|
"avoid-inline-spacing": { run: (function runInPage(ctx) {
|
|
57545
|
-
const { document,
|
|
57546
|
-
const safeRoot = root || document;
|
|
57818
|
+
const { document, helpers, rule } = ctx;
|
|
57547
57819
|
|
|
57548
57820
|
const SPACING_PROPS = ['line-height', 'letter-spacing', 'word-spacing'];
|
|
57549
57821
|
|
|
57550
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[style]'
|
|
57822
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[style]') : helpers.queryAll('[style]');
|
|
57551
57823
|
|
|
57552
57824
|
const occurrences = [];
|
|
57553
57825
|
let applicableCount = 0;
|
|
@@ -57600,11 +57872,10 @@ if (isAccTreeEligible) {
|
|
|
57600
57872
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
57601
57873
|
}), applicability: null },
|
|
57602
57874
|
"binary-control-name-present": { run: (function runInPage(ctx) {
|
|
57603
|
-
const { document,
|
|
57875
|
+
const { document, helpers, rule } = ctx;
|
|
57604
57876
|
const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
|
|
57605
57877
|
? helpers.getEligibilityInfo
|
|
57606
57878
|
: null;
|
|
57607
|
-
const safeRoot = root || document;
|
|
57608
57879
|
|
|
57609
57880
|
|
|
57610
57881
|
function normalizeWs(s) {
|
|
@@ -57736,7 +58007,7 @@ if (isAccTreeEligible) {
|
|
|
57736
58007
|
let applicableCount = 0;
|
|
57737
58008
|
|
|
57738
58009
|
const selector = 'input[type="checkbox"], input[type="radio"], [role="checkbox"], [role="radio"], [role="switch"]';
|
|
57739
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
58010
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
57740
58011
|
|
|
57741
58012
|
|
|
57742
58013
|
// Precompute label[for] associations once for speed/determinism.
|
|
@@ -57868,8 +58139,7 @@ if (isAccTreeEligible) {
|
|
|
57868
58139
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
57869
58140
|
}), applicability: null },
|
|
57870
58141
|
"button-name-present": { run: (function runInPage(ctx) {
|
|
57871
|
-
const { document,
|
|
57872
|
-
const safeRoot = root || document;
|
|
58142
|
+
const { document, helpers, rule } = ctx;
|
|
57873
58143
|
|
|
57874
58144
|
const occurrences = [];
|
|
57875
58145
|
let applicableCount = 0;
|
|
@@ -57906,7 +58176,7 @@ if (isAccTreeEligible) {
|
|
|
57906
58176
|
}
|
|
57907
58177
|
|
|
57908
58178
|
const selector = 'button, input[type="button"], input[type="submit"], input[type="reset"], [role="button"]';
|
|
57909
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
58179
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
57910
58180
|
|
|
57911
58181
|
for (const el of nodes) {
|
|
57912
58182
|
// isAccTreeEligible returns { eligible, reasons }, not a boolean.
|
|
@@ -57918,6 +58188,7 @@ if (isAccTreeEligible) {
|
|
|
57918
58188
|
|
|
57919
58189
|
const tag = (el.tagName || '').toLowerCase();
|
|
57920
58190
|
const role = el.getAttribute ? el.getAttribute('role') : null;
|
|
58191
|
+
const roleNorm = normalizeWs(role).toLowerCase();
|
|
57921
58192
|
|
|
57922
58193
|
const nameInfo = helpers.getAccessibleNameInfo ? helpers.getAccessibleNameInfo(el, ctx) : null;
|
|
57923
58194
|
|
|
@@ -57933,7 +58204,17 @@ if (isAccTreeEligible) {
|
|
|
57933
58204
|
inputValueName = getInputButtonValueName(el);
|
|
57934
58205
|
}
|
|
57935
58206
|
|
|
57936
|
-
|
|
58207
|
+
// A native <button> (or [role="button"]) whose role has been overridden to
|
|
58208
|
+
// one of these roles is no longer semantically a button — per the WAI-ARIA
|
|
58209
|
+
// Accessible Name and Description Computation spec these roles are
|
|
58210
|
+
// name-from-author-only, and their rendered content represents a VALUE,
|
|
58211
|
+
// not a NAME (mirrors axe-core's controlValueRoles, verified against its
|
|
58212
|
+
// source). Found on a real page (Spotify's "sort by" control): a
|
|
58213
|
+
// <button role="combobox">List</button> where "List" is the combobox's
|
|
58214
|
+
// currently selected value, not a label for what the combobox is —
|
|
58215
|
+
// crediting it as the accessible name masked a real missing-name bug.
|
|
58216
|
+
const VALUE_ROLES = ['textbox', 'progressbar', 'scrollbar', 'slider', 'spinbutton', 'combobox', 'listbox'];
|
|
58217
|
+
const isContentNameCandidate = (tag === 'button' || role === 'button') && !VALUE_ROLES.includes(roleNorm);
|
|
57937
58218
|
const contentName =
|
|
57938
58219
|
(!trustedProgrammaticName && !inputValueName && isContentNameCandidate)
|
|
57939
58220
|
? getConservativeSubtreeText(el)
|
|
@@ -57993,12 +58274,41 @@ if (isAccTreeEligible) {
|
|
|
57993
58274
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
57994
58275
|
}
|
|
57995
58276
|
|
|
57996
|
-
function
|
|
58277
|
+
const isAccTreeEligible = helpers && typeof helpers.isAccTreeEligible === 'function' ? helpers.isAccTreeEligible : null;
|
|
58278
|
+
|
|
58279
|
+
function isExposedToAt(el) {
|
|
58280
|
+
if (!isAccTreeEligible) return true;
|
|
57997
58281
|
try {
|
|
57998
|
-
|
|
58282
|
+
const r = isAccTreeEligible(el, ctx);
|
|
58283
|
+
if (typeof r === 'boolean') return r;
|
|
58284
|
+
return !!(r && r.eligible);
|
|
57999
58285
|
} catch {
|
|
58000
|
-
return
|
|
58286
|
+
return true;
|
|
58287
|
+
}
|
|
58288
|
+
}
|
|
58289
|
+
|
|
58290
|
+
function queryAll(selector) {
|
|
58291
|
+
try {
|
|
58292
|
+
return helpers && typeof helpers.queryAllSmart === 'function'
|
|
58293
|
+
? helpers.queryAllSmart(selector)
|
|
58294
|
+
: document.querySelectorAll(selector);
|
|
58295
|
+
} catch {
|
|
58296
|
+
return [];
|
|
58297
|
+
}
|
|
58298
|
+
}
|
|
58299
|
+
|
|
58300
|
+
// Filters through isAccTreeEligible (hidden/aria-hidden/display:none/inert
|
|
58301
|
+
// don't count as "a bypass mechanism is present") -- see page-has-heading-
|
|
58302
|
+
// one-manual.js's identical fix for the real-world trigger (CDC's flu
|
|
58303
|
+
// page, 2026-07-30: its only <h1> sits inside a display:none ancestor,
|
|
58304
|
+
// unreachable by sighted and screen reader users alike). A fully
|
|
58305
|
+
// non-rendered <main>/heading was previously credited here too, wrongly
|
|
58306
|
+
// returning `pass` for a page with zero actual bypass mechanisms.
|
|
58307
|
+
function hasMainLandmark() {
|
|
58308
|
+
for (const el of queryAll('main, [role="main"]')) {
|
|
58309
|
+
if (el && isExposedToAt(el)) return true;
|
|
58001
58310
|
}
|
|
58311
|
+
return false;
|
|
58002
58312
|
}
|
|
58003
58313
|
|
|
58004
58314
|
function hasWorkingAnchorLink() {
|
|
@@ -58040,11 +58350,10 @@ if (isAccTreeEligible) {
|
|
|
58040
58350
|
}
|
|
58041
58351
|
|
|
58042
58352
|
function hasHeading() {
|
|
58043
|
-
|
|
58044
|
-
|
|
58045
|
-
} catch {
|
|
58046
|
-
return false;
|
|
58353
|
+
for (const el of queryAll('h1, h2, h3, h4, h5, h6, [role="heading"]')) {
|
|
58354
|
+
if (el && isExposedToAt(el)) return true;
|
|
58047
58355
|
}
|
|
58356
|
+
return false;
|
|
58048
58357
|
}
|
|
58049
58358
|
|
|
58050
58359
|
const mainLandmark = hasMainLandmark();
|
|
@@ -58302,11 +58611,10 @@ if (isAccTreeEligible) {
|
|
|
58302
58611
|
return { ruleId: rule.ruleId, outcome: 'cantTell', severity: 'minor', occurrences };
|
|
58303
58612
|
}), applicability: null },
|
|
58304
58613
|
"combobox-name-present": { run: (function runInPage(ctx) {
|
|
58305
|
-
const { document,
|
|
58614
|
+
const { document, helpers, rule } = ctx;
|
|
58306
58615
|
const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
|
|
58307
58616
|
? helpers.getEligibilityInfo
|
|
58308
58617
|
: null;
|
|
58309
|
-
const safeRoot = root || document;
|
|
58310
58618
|
|
|
58311
58619
|
|
|
58312
58620
|
function normalizeWs(s) {
|
|
@@ -58438,7 +58746,7 @@ if (isAccTreeEligible) {
|
|
|
58438
58746
|
let applicableCount = 0;
|
|
58439
58747
|
|
|
58440
58748
|
const selector = '[role="combobox"]';
|
|
58441
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
58749
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
58442
58750
|
|
|
58443
58751
|
// Precompute label[for] map for combobox elements that are labelable
|
|
58444
58752
|
// native form controls (e.g. <input role="combobox">).
|
|
@@ -60165,14 +60473,13 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
60165
60473
|
return { ruleId: rule.ruleId, outcome: 'fail', severity: rule.defaultSeverity || 'serious', occurrences };
|
|
60166
60474
|
}), applicability: null },
|
|
60167
60475
|
"definition-list-children-valid": { run: (function runInPage(ctx) {
|
|
60168
|
-
const { document,
|
|
60169
|
-
const safeRoot = root || document;
|
|
60476
|
+
const { document, helpers, rule } = ctx;
|
|
60170
60477
|
|
|
60171
60478
|
// Declared inside runInPage — see scripts/build-core.js header
|
|
60172
60479
|
// ("runInPage MUST be self-contained").
|
|
60173
60480
|
const PASSTHROUGH_TAGS = new Set(['dt', 'dd', 'script', 'template', 'style']);
|
|
60174
60481
|
|
|
60175
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('dl'
|
|
60482
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('dl') : helpers.queryAll('dl');
|
|
60176
60483
|
|
|
60177
60484
|
const occurrences = [];
|
|
60178
60485
|
let applicableCount = 0;
|
|
@@ -60256,10 +60563,9 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
60256
60563
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
60257
60564
|
}), applicability: null },
|
|
60258
60565
|
"deprecated-elements-not-used": { run: (function runInPage(ctx) {
|
|
60259
|
-
const { document,
|
|
60260
|
-
const safeRoot = root || document;
|
|
60566
|
+
const { document, helpers, rule } = ctx;
|
|
60261
60567
|
|
|
60262
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('blink, marquee'
|
|
60568
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('blink, marquee') : helpers.queryAll('blink, marquee');
|
|
60263
60569
|
|
|
60264
60570
|
const occurrences = [];
|
|
60265
60571
|
let applicableCount = 0;
|
|
@@ -60298,11 +60604,10 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
60298
60604
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
60299
60605
|
}), applicability: null },
|
|
60300
60606
|
"dialog-name-present": { run: (function runInPage(ctx) {
|
|
60301
|
-
const { document,
|
|
60607
|
+
const { document, helpers, rule } = ctx;
|
|
60302
60608
|
const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
|
|
60303
60609
|
? helpers.getEligibilityInfo
|
|
60304
60610
|
: null;
|
|
60305
|
-
const safeRoot = root || document;
|
|
60306
60611
|
|
|
60307
60612
|
|
|
60308
60613
|
function normalizeWs(s) {
|
|
@@ -60416,7 +60721,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
60416
60721
|
let applicableCount = 0;
|
|
60417
60722
|
|
|
60418
60723
|
const selector = '[role="dialog"],[role="alertdialog"]';
|
|
60419
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
60724
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
60420
60725
|
|
|
60421
60726
|
|
|
60422
60727
|
function evaluate(el) {
|
|
@@ -60481,10 +60786,9 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
60481
60786
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
60482
60787
|
}), applicability: null },
|
|
60483
60788
|
"dlitem-parent-valid": { run: (function runInPage(ctx) {
|
|
60484
|
-
const { document,
|
|
60485
|
-
const safeRoot = root || document;
|
|
60789
|
+
const { document, helpers, rule } = ctx;
|
|
60486
60790
|
|
|
60487
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('dt, dd'
|
|
60791
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('dt, dd') : helpers.queryAll('dt, dd');
|
|
60488
60792
|
|
|
60489
60793
|
const occurrences = [];
|
|
60490
60794
|
let applicableCount = 0;
|
|
@@ -61136,8 +61440,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
61136
61440
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
61137
61441
|
}), applicability: null },
|
|
61138
61442
|
"focus-order-semantics": { run: (function runInPage(ctx) {
|
|
61139
|
-
const { document,
|
|
61140
|
-
const safeRoot = root || document;
|
|
61443
|
+
const { document, helpers, rule } = ctx;
|
|
61141
61444
|
|
|
61142
61445
|
const NON_INTERACTIVE_ROLES = new Set([
|
|
61143
61446
|
'generic', 'group', 'text', 'presentation', 'none', 'img', 'heading',
|
|
@@ -61147,7 +61450,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
61147
61450
|
'definition', 'paragraph', 'caption', 'status', 'alert', 'log', 'tooltip'
|
|
61148
61451
|
]);
|
|
61149
61452
|
|
|
61150
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[tabindex][role]'
|
|
61453
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[tabindex][role]') : helpers.queryAll('[tabindex][role]');
|
|
61151
61454
|
|
|
61152
61455
|
const occurrences = [];
|
|
61153
61456
|
let applicableCount = 0;
|
|
@@ -61788,11 +62091,10 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
61788
62091
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [], data: { details: { metrics } } };
|
|
61789
62092
|
}), applicability: null },
|
|
61790
62093
|
"form-control-single-label": { run: (function runInPage(ctx) {
|
|
61791
|
-
const { document,
|
|
61792
|
-
const safeRoot = root || document;
|
|
62094
|
+
const { document, helpers, rule } = ctx;
|
|
61793
62095
|
|
|
61794
62096
|
const selector = 'input:not([type="hidden"]):not([type="submit"]):not([type="reset"]):not([type="button"]):not([type="image"]),select,textarea';
|
|
61795
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
62097
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
61796
62098
|
|
|
61797
62099
|
// Build a for-value -> label[] map once (avoids per-control dynamic
|
|
61798
62100
|
// attribute-selector construction / CSS.escape, which is not guaranteed
|
|
@@ -62070,14 +62372,13 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
62070
62372
|
return { ruleId: rule.ruleId, outcome: 'fail', severity: rule.defaultSeverity || 'serious', occurrences };
|
|
62071
62373
|
}), applicability: null },
|
|
62072
62374
|
"identical-links-same-purpose": { run: (function runInPage(ctx) {
|
|
62073
|
-
const { document,
|
|
62074
|
-
const safeRoot = root || document;
|
|
62375
|
+
const { document, helpers, rule } = ctx;
|
|
62075
62376
|
|
|
62076
62377
|
function normName(s) {
|
|
62077
62378
|
return (s == null ? '' : String(s)).replace(/\s+/g, ' ').trim().toLowerCase();
|
|
62078
62379
|
}
|
|
62079
62380
|
|
|
62080
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('a[href]'
|
|
62381
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('a[href]') : helpers.queryAll('a[href]');
|
|
62081
62382
|
|
|
62082
62383
|
const groups = new Map(); // normName -> [{ el, href }]
|
|
62083
62384
|
let applicableCount = 0;
|
|
@@ -62157,8 +62458,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
62157
62458
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
62158
62459
|
}), applicability: null },
|
|
62159
62460
|
"iframe-focusable-content": { run: (function runInPage(ctx) {
|
|
62160
|
-
const { document,
|
|
62161
|
-
const safeRoot = root || document;
|
|
62461
|
+
const { document, helpers, rule } = ctx;
|
|
62162
62462
|
|
|
62163
62463
|
function hasFocusableCandidate(doc) {
|
|
62164
62464
|
if (!doc || !doc.querySelectorAll) return false;
|
|
@@ -62190,7 +62490,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
62190
62490
|
return !Number.isNaN(n) && n < 0;
|
|
62191
62491
|
}
|
|
62192
62492
|
|
|
62193
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('iframe, frame'
|
|
62493
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('iframe, frame') : helpers.queryAll('iframe, frame');
|
|
62194
62494
|
|
|
62195
62495
|
const occurrences = [];
|
|
62196
62496
|
let applicableCount = 0;
|
|
@@ -62240,10 +62540,9 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
62240
62540
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
62241
62541
|
}), applicability: null },
|
|
62242
62542
|
"iframe-name-present": { run: (function runInPage(ctx) {
|
|
62243
|
-
const { document,
|
|
62244
|
-
const safeRoot = root || document;
|
|
62543
|
+
const { document, helpers, rule } = ctx;
|
|
62245
62544
|
|
|
62246
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('iframe, frame'
|
|
62545
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('iframe, frame') : helpers.queryAll('iframe, frame');
|
|
62247
62546
|
|
|
62248
62547
|
const occurrences = [];
|
|
62249
62548
|
let applicableCount = 0;
|
|
@@ -62295,10 +62594,9 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
62295
62594
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
62296
62595
|
}), applicability: null },
|
|
62297
62596
|
"iframe-title-unique": { run: (function runInPage(ctx) {
|
|
62298
|
-
const { document,
|
|
62299
|
-
const safeRoot = root || document;
|
|
62597
|
+
const { document, helpers, rule } = ctx;
|
|
62300
62598
|
|
|
62301
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('iframe, frame'
|
|
62599
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('iframe, frame') : helpers.queryAll('iframe, frame');
|
|
62302
62600
|
|
|
62303
62601
|
const groups = new Map(); // trimmed title -> elements[]
|
|
62304
62602
|
let applicableCount = 0;
|
|
@@ -62353,8 +62651,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
62353
62651
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
62354
62652
|
}), applicability: null },
|
|
62355
62653
|
"image-redundant-alt": { run: (function runInPage(ctx) {
|
|
62356
|
-
const { document,
|
|
62357
|
-
const safeRoot = root || document;
|
|
62654
|
+
const { document, helpers, rule } = ctx;
|
|
62358
62655
|
|
|
62359
62656
|
function normalizeWs(s) {
|
|
62360
62657
|
return String(s || '').replace(/\s+/g, ' ').trim();
|
|
@@ -62373,7 +62670,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
62373
62670
|
return normalizeWs(text);
|
|
62374
62671
|
}
|
|
62375
62672
|
|
|
62376
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('img[alt]'
|
|
62673
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('img[alt]') : helpers.queryAll('img[alt]');
|
|
62377
62674
|
|
|
62378
62675
|
const occurrences = [];
|
|
62379
62676
|
let applicableCount = 0;
|
|
@@ -63161,8 +63458,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
63161
63458
|
return { ruleId: rule.ruleId, outcome: 'cantTell', severity: 'minor', occurrences };
|
|
63162
63459
|
}), applicability: null },
|
|
63163
63460
|
"label-in-name": { run: (function runInPage(ctx) {
|
|
63164
|
-
const { document,
|
|
63165
|
-
const safeRoot = root || document;
|
|
63461
|
+
const { document, helpers, rule } = ctx;
|
|
63166
63462
|
|
|
63167
63463
|
const occurrences = [];
|
|
63168
63464
|
let applicableCount = 0;
|
|
@@ -63171,7 +63467,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
63171
63467
|
// NOTE: aria-hidden is intentionally NOT excluded here; it does not affect visual rendering.
|
|
63172
63468
|
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])';
|
|
63173
63469
|
|
|
63174
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
63470
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
63175
63471
|
|
|
63176
63472
|
function norm(s) {
|
|
63177
63473
|
const v = (s == null ? '' : String(s));
|
|
@@ -63309,11 +63605,11 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
63309
63605
|
}
|
|
63310
63606
|
|
|
63311
63607
|
// Fallback: label[for=id]. Uses `document` directly rather than
|
|
63312
|
-
// ctx.root
|
|
63313
|
-
//
|
|
63314
|
-
//
|
|
63315
|
-
//
|
|
63316
|
-
//
|
|
63608
|
+
// ctx.root -- label[for] association is a document-wide relationship
|
|
63609
|
+
// (IDs are document-unique), not bounded by whatever contextSelector
|
|
63610
|
+
// region happens to be scanned, and ctx.root is an array (multi-region
|
|
63611
|
+
// contextSelector support), not a single element with its own
|
|
63612
|
+
// .querySelector to call directly.
|
|
63317
63613
|
try {
|
|
63318
63614
|
const idAttribute = control && control.getAttribute ? (control.getAttribute('id') || '') : '';
|
|
63319
63615
|
const key = String(idAttribute || '').trim();
|
|
@@ -63454,11 +63750,10 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
63454
63750
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
63455
63751
|
}), applicability: null },
|
|
63456
63752
|
"label-title-only": { run: (function runInPage(ctx) {
|
|
63457
|
-
const { document,
|
|
63458
|
-
const safeRoot = root || document;
|
|
63753
|
+
const { document, helpers, rule } = ctx;
|
|
63459
63754
|
|
|
63460
63755
|
const selector = 'input:not([type="hidden"]):not([type="submit"]):not([type="reset"]):not([type="button"]):not([type="image"]),select,textarea';
|
|
63461
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
63756
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
63462
63757
|
|
|
63463
63758
|
const labelsByFor = new Map();
|
|
63464
63759
|
const allLabels = document.getElementsByTagName ? document.getElementsByTagName('label') : [];
|
|
@@ -64505,8 +64800,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
64505
64800
|
return { ruleId: rule.ruleId, outcome: 'cantTell', severity: rule.defaultSeverity || 'minor', occurrences };
|
|
64506
64801
|
}), applicability: null },
|
|
64507
64802
|
"link-in-text-block": { run: (function runInPage(ctx) {
|
|
64508
|
-
const { document,
|
|
64509
|
-
const safeRoot = root || document;
|
|
64803
|
+
const { document, helpers, rule, engineOptions } = ctx;
|
|
64510
64804
|
|
|
64511
64805
|
function safeComputedStyle(el) {
|
|
64512
64806
|
try {
|
|
@@ -64552,7 +64846,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
64552
64846
|
const c = helpers && helpers.contrast ? helpers.contrast : null;
|
|
64553
64847
|
|
|
64554
64848
|
const selector = 'a[href]';
|
|
64555
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
64849
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
64556
64850
|
|
|
64557
64851
|
const occurrences = [];
|
|
64558
64852
|
let applicableCount = 0;
|
|
@@ -64652,8 +64946,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
64652
64946
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
64653
64947
|
}), applicability: null },
|
|
64654
64948
|
"link-name-present": { run: (function runInPage(ctx) {
|
|
64655
|
-
const { document,
|
|
64656
|
-
const safeRoot = root || document;
|
|
64949
|
+
const { document, helpers, rule } = ctx;
|
|
64657
64950
|
|
|
64658
64951
|
const occurrences = [];
|
|
64659
64952
|
let applicableCount = 0;
|
|
@@ -64674,7 +64967,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
64674
64967
|
}
|
|
64675
64968
|
|
|
64676
64969
|
const selector = 'a[href], area[href], [role="link"]';
|
|
64677
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
64970
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
64678
64971
|
|
|
64679
64972
|
for (const el of nodes) {
|
|
64680
64973
|
// isAccTreeEligible returns { eligible, reasons }, not a boolean.
|
|
@@ -64687,7 +64980,20 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
64687
64980
|
const nameInfo = helpers.getAccessibleNameInfo ? helpers.getAccessibleNameInfo(el, ctx) : null;
|
|
64688
64981
|
const programmaticName = (nameInfo && typeof nameInfo.value === 'string') ? nameInfo.value : '';
|
|
64689
64982
|
|
|
64690
|
-
|
|
64983
|
+
// A native <a href>/<area href> (or [role="link"]) whose role has been
|
|
64984
|
+
// overridden to one of these roles is no longer semantically a link —
|
|
64985
|
+
// per the WAI-ARIA Accessible Name and Description Computation spec
|
|
64986
|
+
// these roles are name-from-author-only, and their rendered content
|
|
64987
|
+
// represents a VALUE, not a NAME (mirrors axe-core's controlValueRoles,
|
|
64988
|
+
// verified against its source; see button-name-present.js's identical
|
|
64989
|
+
// fix for a real-world example — <button role="combobox">List</button> —
|
|
64990
|
+
// of the same class of bug on a different host tag).
|
|
64991
|
+
const role = el.getAttribute ? el.getAttribute('role') : null;
|
|
64992
|
+
const roleNorm = String(role || '').replace(/\s+/g, ' ').trim().toLowerCase();
|
|
64993
|
+
const VALUE_ROLES = ['textbox', 'progressbar', 'scrollbar', 'slider', 'spinbutton', 'combobox', 'listbox'];
|
|
64994
|
+
const isContentNameCandidate = !VALUE_ROLES.includes(roleNorm);
|
|
64995
|
+
|
|
64996
|
+
const contentName = (programmaticName.trim().length === 0 && isContentNameCandidate) ? getConservativeSubtreeText(el) : '';
|
|
64691
64997
|
const finalName = (programmaticName.trim().length ? programmaticName : contentName).trim();
|
|
64692
64998
|
|
|
64693
64999
|
if (finalName.length === 0) {
|
|
@@ -64739,8 +65045,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
64739
65045
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
64740
65046
|
}), applicability: null },
|
|
64741
65047
|
"link-name-quality": { run: (function runInPage(ctx) {
|
|
64742
|
-
const { document,
|
|
64743
|
-
const safeRoot = root || document;
|
|
65048
|
+
const { document, helpers, rule } = ctx;
|
|
64744
65049
|
|
|
64745
65050
|
const GENERIC_LINK_TEXT = new Set([
|
|
64746
65051
|
'click here', 'here', 'click', 'more', 'more info', 'more information',
|
|
@@ -64759,7 +65064,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
64759
65064
|
}
|
|
64760
65065
|
|
|
64761
65066
|
const selector = 'a[href]';
|
|
64762
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
65067
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
64763
65068
|
|
|
64764
65069
|
const occurrences = [];
|
|
64765
65070
|
let applicableCount = 0;
|
|
@@ -64819,14 +65124,13 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
64819
65124
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
64820
65125
|
}), applicability: null },
|
|
64821
65126
|
"list-children-valid": { run: (function runInPage(ctx) {
|
|
64822
|
-
const { document,
|
|
64823
|
-
const safeRoot = root || document;
|
|
65127
|
+
const { document, helpers, rule } = ctx;
|
|
64824
65128
|
|
|
64825
65129
|
// Declared inside runInPage — see scripts/build-core.js header
|
|
64826
65130
|
// ("runInPage MUST be self-contained").
|
|
64827
65131
|
const ALLOWED_CHILD_TAGS = new Set(['li', 'script', 'template']);
|
|
64828
65132
|
|
|
64829
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('ul, ol'
|
|
65133
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('ul, ol') : helpers.queryAll('ul, ol');
|
|
64830
65134
|
|
|
64831
65135
|
const isAccTreeEligible = helpers && typeof helpers.isAccTreeEligible === 'function' ? helpers.isAccTreeEligible : null;
|
|
64832
65136
|
|
|
@@ -64897,11 +65201,10 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
64897
65201
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
64898
65202
|
}), applicability: null },
|
|
64899
65203
|
"listbox-name-present": { run: (function runInPage(ctx) {
|
|
64900
|
-
const { document,
|
|
65204
|
+
const { document, helpers, rule } = ctx;
|
|
64901
65205
|
const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
|
|
64902
65206
|
? helpers.getEligibilityInfo
|
|
64903
65207
|
: null;
|
|
64904
|
-
const safeRoot = root || document;
|
|
64905
65208
|
|
|
64906
65209
|
|
|
64907
65210
|
function normalizeWs(s) {
|
|
@@ -65006,7 +65309,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
65006
65309
|
let applicableCount = 0;
|
|
65007
65310
|
|
|
65008
65311
|
const selector = "[role=\"listbox\"]";
|
|
65009
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
65312
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
65010
65313
|
|
|
65011
65314
|
// Precompute label[for] map for listbox elements that are labelable
|
|
65012
65315
|
// native form controls (e.g. <select multiple role="listbox">).
|
|
@@ -65107,10 +65410,9 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
65107
65410
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
65108
65411
|
}), applicability: null },
|
|
65109
65412
|
"listitem-parent-valid": { run: (function runInPage(ctx) {
|
|
65110
|
-
const { document,
|
|
65111
|
-
const safeRoot = root || document;
|
|
65413
|
+
const { document, helpers, rule } = ctx;
|
|
65112
65414
|
|
|
65113
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('li'
|
|
65415
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('li') : helpers.queryAll('li');
|
|
65114
65416
|
|
|
65115
65417
|
const occurrences = [];
|
|
65116
65418
|
let applicableCount = 0;
|
|
@@ -65539,11 +65841,10 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
65539
65841
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
65540
65842
|
}), applicability: null },
|
|
65541
65843
|
"menuitem-name-present": { run: (function runInPage(ctx) {
|
|
65542
|
-
const { document,
|
|
65844
|
+
const { document, helpers, rule } = ctx;
|
|
65543
65845
|
const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
|
|
65544
65846
|
? helpers.getEligibilityInfo
|
|
65545
65847
|
: null;
|
|
65546
|
-
const safeRoot = root || document;
|
|
65547
65848
|
|
|
65548
65849
|
|
|
65549
65850
|
function normalizeWs(s) {
|
|
@@ -65649,7 +65950,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
65649
65950
|
let applicableCount = 0;
|
|
65650
65951
|
|
|
65651
65952
|
const selector = '[role="menuitem"],[role="menuitemcheckbox"],[role="menuitemradio"]';
|
|
65652
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
65953
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
65653
65954
|
|
|
65654
65955
|
|
|
65655
65956
|
function evaluate(el) {
|
|
@@ -65955,11 +66256,10 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
65955
66256
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
65956
66257
|
}), applicability: null },
|
|
65957
66258
|
"meter-name-present": { run: (function runInPage(ctx) {
|
|
65958
|
-
const { document,
|
|
66259
|
+
const { document, helpers, rule } = ctx;
|
|
65959
66260
|
const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
|
|
65960
66261
|
? helpers.getEligibilityInfo
|
|
65961
66262
|
: null;
|
|
65962
|
-
const safeRoot = root || document;
|
|
65963
66263
|
|
|
65964
66264
|
|
|
65965
66265
|
function normalizeWs(s) {
|
|
@@ -66023,7 +66323,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
66023
66323
|
let applicableCount = 0;
|
|
66024
66324
|
|
|
66025
66325
|
const selector = '[role="meter"]';
|
|
66026
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
66326
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
66027
66327
|
|
|
66028
66328
|
|
|
66029
66329
|
function evaluate(el) {
|
|
@@ -66089,8 +66389,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
66089
66389
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
66090
66390
|
}), applicability: null },
|
|
66091
66391
|
"mouse-only-event-handlers": { run: (function runInPage(ctx) {
|
|
66092
|
-
const { document,
|
|
66093
|
-
const safeRoot = root || document;
|
|
66392
|
+
const { document, helpers, rule } = ctx;
|
|
66094
66393
|
|
|
66095
66394
|
const MOUSE_ONLY_ATTRS = [
|
|
66096
66395
|
'onmouseover', 'onmouseout', 'onmousedown', 'onmouseup',
|
|
@@ -66101,7 +66400,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
66101
66400
|
function trim(v) { return (v == null ? '' : String(v)).trim(); }
|
|
66102
66401
|
|
|
66103
66402
|
const selector = MOUSE_ONLY_ATTRS.map((a) => `[${a}]`).join(', ');
|
|
66104
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
66403
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
66105
66404
|
|
|
66106
66405
|
const occurrences = [];
|
|
66107
66406
|
let applicableCount = 0;
|
|
@@ -66153,8 +66452,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
66153
66452
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
66154
66453
|
}), applicability: null },
|
|
66155
66454
|
"nested-interactive-controls-absent": { run: (function runInPage(ctx) {
|
|
66156
|
-
const { document,
|
|
66157
|
-
const safeRoot = root || document;
|
|
66455
|
+
const { document, helpers, rule } = ctx;
|
|
66158
66456
|
|
|
66159
66457
|
// Declared inside runInPage — see scripts/build-core.js header
|
|
66160
66458
|
// ("runInPage MUST be self-contained").
|
|
@@ -66183,7 +66481,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
66183
66481
|
'[role="treeitem"]'
|
|
66184
66482
|
].join(', ');
|
|
66185
66483
|
|
|
66186
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(INTERACTIVE_SELECTOR
|
|
66484
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(INTERACTIVE_SELECTOR) : helpers.queryAll(INTERACTIVE_SELECTOR);
|
|
66187
66485
|
|
|
66188
66486
|
const occurrences = [];
|
|
66189
66487
|
let applicableCount = 0;
|
|
@@ -66232,10 +66530,9 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
66232
66530
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
66233
66531
|
}), applicability: null },
|
|
66234
66532
|
"no-autoplay-audio": { run: (function runInPage(ctx) {
|
|
66235
|
-
const { document,
|
|
66236
|
-
const safeRoot = root || document;
|
|
66533
|
+
const { document, helpers, rule } = ctx;
|
|
66237
66534
|
|
|
66238
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('audio[autoplay], video[autoplay]'
|
|
66535
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('audio[autoplay], video[autoplay]') : helpers.queryAll('audio[autoplay], video[autoplay]');
|
|
66239
66536
|
|
|
66240
66537
|
const occurrences = [];
|
|
66241
66538
|
let applicableCount = 0;
|
|
@@ -66589,11 +66886,10 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
66589
66886
|
return {ruleId: rule.ruleId, outcome: 'cantTell', severity: 'minor', occurrences};
|
|
66590
66887
|
}), applicability: null },
|
|
66591
66888
|
"option-name-present": { run: (function runInPage(ctx) {
|
|
66592
|
-
const { document,
|
|
66889
|
+
const { document, helpers, rule } = ctx;
|
|
66593
66890
|
const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
|
|
66594
66891
|
? helpers.getEligibilityInfo
|
|
66595
66892
|
: null;
|
|
66596
|
-
const safeRoot = root || document;
|
|
66597
66893
|
|
|
66598
66894
|
|
|
66599
66895
|
function normalizeWs(s) {
|
|
@@ -66657,7 +66953,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
66657
66953
|
let applicableCount = 0;
|
|
66658
66954
|
|
|
66659
66955
|
const selector = "[role=\"option\"]";
|
|
66660
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
66956
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
66661
66957
|
|
|
66662
66958
|
|
|
66663
66959
|
function hasName(el) {
|
|
@@ -66720,8 +67016,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
66720
67016
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
66721
67017
|
}), applicability: null },
|
|
66722
67018
|
"p-as-heading": { run: (function runInPage(ctx) {
|
|
66723
|
-
const { document,
|
|
66724
|
-
const safeRoot = root || document;
|
|
67019
|
+
const { document, helpers, rule } = ctx;
|
|
66725
67020
|
|
|
66726
67021
|
const MAX_HEADING_LIKE_CHARS = 120;
|
|
66727
67022
|
const MIN_FONT_SIZE_PX = 18;
|
|
@@ -66773,7 +67068,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
66773
67068
|
return Number.isFinite(px) ? px : 0;
|
|
66774
67069
|
}
|
|
66775
67070
|
|
|
66776
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('p'
|
|
67071
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('p') : helpers.queryAll('p');
|
|
66777
67072
|
|
|
66778
67073
|
const occurrences = [];
|
|
66779
67074
|
let applicableCount = 0;
|
|
@@ -66855,14 +67150,31 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
66855
67150
|
return tag === 'h1';
|
|
66856
67151
|
}
|
|
66857
67152
|
|
|
67153
|
+
const isAccTreeEligible = helpers && typeof helpers.isAccTreeEligible === 'function' ? helpers.isAccTreeEligible : null;
|
|
67154
|
+
|
|
67155
|
+
function isExposedToAt(el) {
|
|
67156
|
+
if (!isAccTreeEligible) return true;
|
|
67157
|
+
try {
|
|
67158
|
+
const r = isAccTreeEligible(el, ctx);
|
|
67159
|
+
if (typeof r === 'boolean') return r;
|
|
67160
|
+
return !!(r && r.eligible);
|
|
67161
|
+
} catch {
|
|
67162
|
+
return true;
|
|
67163
|
+
}
|
|
67164
|
+
}
|
|
67165
|
+
|
|
67166
|
+
// queryAllSmart (shadow-DOM-aware) instead of plain document.querySelectorAll -- see
|
|
67167
|
+
// landmark-one-main-manual.js's identical precedent.
|
|
66858
67168
|
let nodes = [];
|
|
66859
67169
|
try {
|
|
66860
|
-
nodes =
|
|
67170
|
+
nodes = helpers && typeof helpers.queryAllSmart === 'function'
|
|
67171
|
+
? helpers.queryAllSmart('h1, [role]')
|
|
67172
|
+
: document.querySelectorAll('h1, [role]');
|
|
66861
67173
|
} catch {
|
|
66862
67174
|
nodes = [];
|
|
66863
67175
|
}
|
|
66864
67176
|
|
|
66865
|
-
const hasH1 = Array.from(nodes).some((el) => el && isLevelOneHeading(el));
|
|
67177
|
+
const hasH1 = Array.from(nodes).some((el) => el && isLevelOneHeading(el) && isExposedToAt(el));
|
|
66866
67178
|
|
|
66867
67179
|
if (hasH1) {
|
|
66868
67180
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
@@ -67186,8 +67498,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
67186
67498
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
67187
67499
|
}), applicability: null },
|
|
67188
67500
|
"presentation-role-conflict": { run: (function runInPage(ctx) {
|
|
67189
|
-
const { document,
|
|
67190
|
-
const safeRoot = root || document;
|
|
67501
|
+
const { document, helpers, rule } = ctx;
|
|
67191
67502
|
|
|
67192
67503
|
// The full set of ARIA attributes marked `global: true` per the WAI-ARIA
|
|
67193
67504
|
// spec (confirmed against a widely-used reference engine's own
|
|
@@ -67204,8 +67515,8 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
67204
67515
|
const getFocusableInfo = helpers && typeof helpers.getFocusableInfo === 'function' ? helpers.getFocusableInfo : null;
|
|
67205
67516
|
|
|
67206
67517
|
const nodes = helpers.queryAllSmart
|
|
67207
|
-
? helpers.queryAllSmart('[role="presentation"], [role="none"], img[alt=""]'
|
|
67208
|
-
: helpers.queryAll('[role="presentation"], [role="none"], img[alt=""]'
|
|
67518
|
+
? helpers.queryAllSmart('[role="presentation"], [role="none"], img[alt=""]')
|
|
67519
|
+
: helpers.queryAll('[role="presentation"], [role="none"], img[alt=""]');
|
|
67209
67520
|
|
|
67210
67521
|
const occurrences = [];
|
|
67211
67522
|
let applicableCount = 0;
|
|
@@ -67269,11 +67580,10 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
67269
67580
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
67270
67581
|
}), applicability: null },
|
|
67271
67582
|
"progressbar-name-present": { run: (function runInPage(ctx) {
|
|
67272
|
-
const { document,
|
|
67583
|
+
const { document, helpers, rule } = ctx;
|
|
67273
67584
|
const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
|
|
67274
67585
|
? helpers.getEligibilityInfo
|
|
67275
67586
|
: null;
|
|
67276
|
-
const safeRoot = root || document;
|
|
67277
67587
|
|
|
67278
67588
|
|
|
67279
67589
|
function normalizeWs(s) {
|
|
@@ -67337,7 +67647,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
67337
67647
|
let applicableCount = 0;
|
|
67338
67648
|
|
|
67339
67649
|
const selector = '[role="progressbar"]';
|
|
67340
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
67650
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
67341
67651
|
|
|
67342
67652
|
|
|
67343
67653
|
function evaluate(el) {
|
|
@@ -67749,8 +68059,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
67749
68059
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
67750
68060
|
}), applicability: null },
|
|
67751
68061
|
"scrollable-region-focusable": { run: (function runInPage(ctx) {
|
|
67752
|
-
const { document,
|
|
67753
|
-
const safeRoot = root || document;
|
|
68062
|
+
const { document, helpers, rule } = ctx;
|
|
67754
68063
|
|
|
67755
68064
|
function safeComputedStyle(el) {
|
|
67756
68065
|
try {
|
|
@@ -67795,7 +68104,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
67795
68104
|
}
|
|
67796
68105
|
|
|
67797
68106
|
const CANDIDATE_SELECTOR = 'div, section, article, aside, main, nav, pre, table, blockquote, ul, ol, textarea';
|
|
67798
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(CANDIDATE_SELECTOR
|
|
68107
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(CANDIDATE_SELECTOR) : helpers.queryAll(CANDIDATE_SELECTOR);
|
|
67799
68108
|
|
|
67800
68109
|
const occurrences = [];
|
|
67801
68110
|
let applicableCount = 0;
|
|
@@ -67850,11 +68159,10 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
67850
68159
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
67851
68160
|
}), applicability: null },
|
|
67852
68161
|
"searchbox-name-present": { run: (function runInPage(ctx) {
|
|
67853
|
-
const { document,
|
|
68162
|
+
const { document, helpers, rule } = ctx;
|
|
67854
68163
|
const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
|
|
67855
68164
|
? helpers.getEligibilityInfo
|
|
67856
68165
|
: null;
|
|
67857
|
-
const safeRoot = root || document;
|
|
67858
68166
|
|
|
67859
68167
|
|
|
67860
68168
|
function normalizeWs(s) {
|
|
@@ -67959,7 +68267,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
67959
68267
|
let applicableCount = 0;
|
|
67960
68268
|
|
|
67961
68269
|
const selector = "[role=\"searchbox\"]";
|
|
67962
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
68270
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
67963
68271
|
|
|
67964
68272
|
// Precompute label[for] map for searchbox elements that are labelable
|
|
67965
68273
|
// native form controls (e.g. <input role="searchbox">).
|
|
@@ -68060,10 +68368,9 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
68060
68368
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
68061
68369
|
}), applicability: null },
|
|
68062
68370
|
"server-side-image-map-absent": { run: (function runInPage(ctx) {
|
|
68063
|
-
const { document,
|
|
68064
|
-
const safeRoot = root || document;
|
|
68371
|
+
const { document, helpers, rule } = ctx;
|
|
68065
68372
|
|
|
68066
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('img[ismap]'
|
|
68373
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('img[ismap]') : helpers.queryAll('img[ismap]');
|
|
68067
68374
|
|
|
68068
68375
|
const occurrences = [];
|
|
68069
68376
|
let applicableCount = 0;
|
|
@@ -68101,8 +68408,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
68101
68408
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
68102
68409
|
}), applicability: null },
|
|
68103
68410
|
"skip-link": { run: (function runInPage(ctx) {
|
|
68104
|
-
const { document,
|
|
68105
|
-
const safeRoot = root || document;
|
|
68411
|
+
const { document, helpers, rule } = ctx;
|
|
68106
68412
|
|
|
68107
68413
|
function normalizeWs(s) {
|
|
68108
68414
|
return String(s || '').replace(/\s+/g, ' ').trim();
|
|
@@ -68129,7 +68435,31 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
68129
68435
|
return normalizeWs(el.textContent);
|
|
68130
68436
|
}
|
|
68131
68437
|
|
|
68132
|
-
|
|
68438
|
+
function hasReliableGeometrySupport() {
|
|
68439
|
+
const probe = document.documentElement || document.body || null;
|
|
68440
|
+
if (!probe || !probe.getClientRects || !probe.getBoundingClientRect) return false;
|
|
68441
|
+
try {
|
|
68442
|
+
const rects = probe.getClientRects();
|
|
68443
|
+
const rectCount = rects ? rects.length : 0;
|
|
68444
|
+
const r = probe.getBoundingClientRect();
|
|
68445
|
+
const w = r && Number.isFinite(r.width) ? r.width : 0;
|
|
68446
|
+
const h = r && Number.isFinite(r.height) ? r.height : 0;
|
|
68447
|
+
return rectCount > 0 && (w > 0 || h > 0);
|
|
68448
|
+
} catch {
|
|
68449
|
+
return false;
|
|
68450
|
+
}
|
|
68451
|
+
}
|
|
68452
|
+
|
|
68453
|
+
function toEligibility(info) {
|
|
68454
|
+
return {
|
|
68455
|
+
eligible: !!(info && info.eligible),
|
|
68456
|
+
reasons: info && Array.isArray(info.reasons) ? info.reasons.slice(0) : []
|
|
68457
|
+
};
|
|
68458
|
+
}
|
|
68459
|
+
|
|
68460
|
+
const geometrySupported = hasReliableGeometrySupport();
|
|
68461
|
+
|
|
68462
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('a[href]') : helpers.queryAll('a[href]');
|
|
68133
68463
|
|
|
68134
68464
|
const occurrences = [];
|
|
68135
68465
|
let applicableCount = 0;
|
|
@@ -68167,7 +68497,63 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
68167
68497
|
}
|
|
68168
68498
|
}
|
|
68169
68499
|
|
|
68170
|
-
if (target)
|
|
68500
|
+
if (target) {
|
|
68501
|
+
const accEligibility = toEligibility(
|
|
68502
|
+
helpers.getEligibilityInfo
|
|
68503
|
+
? helpers.getEligibilityInfo(target, ctx, { targetSet: 'acc' })
|
|
68504
|
+
: (helpers.isAccTreeEligible ? helpers.isAccTreeEligible(target, ctx) : { eligible: true, reasons: [] })
|
|
68505
|
+
);
|
|
68506
|
+
|
|
68507
|
+
let geometryEligibility = null;
|
|
68508
|
+
let geometryReasonCode = null;
|
|
68509
|
+
if (geometrySupported && helpers.isDomVisibleEligible) {
|
|
68510
|
+
geometryEligibility = toEligibility(
|
|
68511
|
+
helpers.isDomVisibleEligible(target, ctx, { visibilityMode: 'styleAndGeometry', ignoreOpacity: true })
|
|
68512
|
+
);
|
|
68513
|
+
if (!geometryEligibility.eligible && geometryEligibility.reasons.includes('noClientRects')) {
|
|
68514
|
+
geometryReasonCode = 'NO_CLIENT_RECTS';
|
|
68515
|
+
} else if (!geometryEligibility.eligible && geometryEligibility.reasons.includes('zeroArea')) {
|
|
68516
|
+
geometryReasonCode = 'ZERO_AREA_TARGET';
|
|
68517
|
+
}
|
|
68518
|
+
}
|
|
68519
|
+
|
|
68520
|
+
const unusableByAcc = !accEligibility.eligible;
|
|
68521
|
+
const unusableByGeometry = !!geometryReasonCode;
|
|
68522
|
+
if (!unusableByAcc && !unusableByGeometry) continue;
|
|
68523
|
+
|
|
68524
|
+
const stableSelector = helpers.buildSelector ? helpers.buildSelector(el) : 'html';
|
|
68525
|
+
const html = helpers.getOuterHtmlSnippet ? helpers.getOuterHtmlSnippet(el) : (el.outerHTML || '');
|
|
68526
|
+
|
|
68527
|
+
occurrences.push({
|
|
68528
|
+
selector: stableSelector,
|
|
68529
|
+
html,
|
|
68530
|
+
summary: 'This skip link points to a target that exists but is not currently usable.',
|
|
68531
|
+
hint: 'Point this skip link to a target that is exposed and usable as a navigation destination.',
|
|
68532
|
+
i18n: {
|
|
68533
|
+
summaryKey: 'skipLink_summary_unusableTarget_cantTell',
|
|
68534
|
+
hintKey: 'skipLink_hint_unusableTarget_cantTell',
|
|
68535
|
+
params: { href }
|
|
68536
|
+
},
|
|
68537
|
+
data: {
|
|
68538
|
+
details: {
|
|
68539
|
+
reasonCode: 'SKIP_LINK_TARGET_UNUSABLE',
|
|
68540
|
+
href,
|
|
68541
|
+
unusableReasonCode: unusableByAcc ? 'ACC_TREE_INELIGIBLE' : geometryReasonCode,
|
|
68542
|
+
targetSelector: helpers.buildSelector ? helpers.buildSelector(target) : null,
|
|
68543
|
+
geometryCheckEnabled: geometrySupported
|
|
68544
|
+
},
|
|
68545
|
+
visibilityFilter: {
|
|
68546
|
+
targetSet: 'acc',
|
|
68547
|
+
accEligible: accEligibility.eligible,
|
|
68548
|
+
reasons: accEligibility.reasons
|
|
68549
|
+
},
|
|
68550
|
+
targetGeometry: geometryEligibility
|
|
68551
|
+
? { eligible: geometryEligibility.eligible, reasons: geometryEligibility.reasons }
|
|
68552
|
+
: { eligible: null, reasons: [] }
|
|
68553
|
+
}
|
|
68554
|
+
});
|
|
68555
|
+
continue;
|
|
68556
|
+
}
|
|
68171
68557
|
|
|
68172
68558
|
const stableSelector = helpers.buildSelector ? helpers.buildSelector(el) : 'html';
|
|
68173
68559
|
const html = helpers.getOuterHtmlSnippet ? helpers.getOuterHtmlSnippet(el) : (el.outerHTML || '');
|
|
@@ -68197,11 +68583,10 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
68197
68583
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
68198
68584
|
}), applicability: null },
|
|
68199
68585
|
"slider-name-present": { run: (function runInPage(ctx) {
|
|
68200
|
-
const { document,
|
|
68586
|
+
const { document, helpers, rule } = ctx;
|
|
68201
68587
|
const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
|
|
68202
68588
|
? helpers.getEligibilityInfo
|
|
68203
68589
|
: null;
|
|
68204
|
-
const safeRoot = root || document;
|
|
68205
68590
|
|
|
68206
68591
|
|
|
68207
68592
|
function normalizeWs(s) {
|
|
@@ -68333,7 +68718,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
68333
68718
|
let applicableCount = 0;
|
|
68334
68719
|
|
|
68335
68720
|
const selector = 'input[type="range"], [role="slider"]';
|
|
68336
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
68721
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
68337
68722
|
|
|
68338
68723
|
|
|
68339
68724
|
// Precompute label[for] map for native range inputs.
|
|
@@ -68447,11 +68832,10 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
68447
68832
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
68448
68833
|
}), applicability: null },
|
|
68449
68834
|
"spinbutton-name-present": { run: (function runInPage(ctx) {
|
|
68450
|
-
const { document,
|
|
68835
|
+
const { document, helpers, rule } = ctx;
|
|
68451
68836
|
const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
|
|
68452
68837
|
? helpers.getEligibilityInfo
|
|
68453
68838
|
: null;
|
|
68454
|
-
const safeRoot = root || document;
|
|
68455
68839
|
|
|
68456
68840
|
|
|
68457
68841
|
function normalizeWs(s) {
|
|
@@ -68556,7 +68940,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
68556
68940
|
let applicableCount = 0;
|
|
68557
68941
|
|
|
68558
68942
|
const selector = "[role=\"spinbutton\"]";
|
|
68559
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
68943
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
68560
68944
|
|
|
68561
68945
|
// Precompute label[for] map for spinbutton elements that are labelable
|
|
68562
68946
|
// native form controls (e.g. <input role="spinbutton">).
|
|
@@ -68657,11 +69041,10 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
68657
69041
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
68658
69042
|
}), applicability: null },
|
|
68659
69043
|
"summary-name-present": { run: (function runInPage(ctx) {
|
|
68660
|
-
const { document,
|
|
69044
|
+
const { document, helpers, rule } = ctx;
|
|
68661
69045
|
const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
|
|
68662
69046
|
? helpers.getEligibilityInfo
|
|
68663
69047
|
: null;
|
|
68664
|
-
const safeRoot = root || document;
|
|
68665
69048
|
|
|
68666
69049
|
|
|
68667
69050
|
function normalizeWs(s) {
|
|
@@ -68724,7 +69107,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
68724
69107
|
const occurrences = [];
|
|
68725
69108
|
let applicableCount = 0;
|
|
68726
69109
|
|
|
68727
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('summary'
|
|
69110
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('summary') : helpers.queryAll('summary');
|
|
68728
69111
|
|
|
68729
69112
|
function evaluate(el) {
|
|
68730
69113
|
const ariaLabel = getAttr(el, 'aria-label');
|
|
@@ -69363,11 +69746,10 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
69363
69746
|
return {ruleId: rule.ruleId, outcome: 'cantTell', severity: 'minor', occurrences};
|
|
69364
69747
|
}), applicability: null },
|
|
69365
69748
|
"tab-name-present": { run: (function runInPage(ctx) {
|
|
69366
|
-
const { document,
|
|
69749
|
+
const { document, helpers, rule } = ctx;
|
|
69367
69750
|
const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
|
|
69368
69751
|
? helpers.getEligibilityInfo
|
|
69369
69752
|
: null;
|
|
69370
|
-
const safeRoot = root || document;
|
|
69371
69753
|
|
|
69372
69754
|
|
|
69373
69755
|
function normalizeWs(s) {
|
|
@@ -69473,7 +69855,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
69473
69855
|
let applicableCount = 0;
|
|
69474
69856
|
|
|
69475
69857
|
const selector = '[role="tab"]';
|
|
69476
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
69858
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
69477
69859
|
|
|
69478
69860
|
|
|
69479
69861
|
function evaluate(el) {
|
|
@@ -69588,14 +69970,13 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
69588
69970
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
69589
69971
|
}), applicability: null },
|
|
69590
69972
|
"table-duplicate-name": { run: (function runInPage(ctx) {
|
|
69591
|
-
const { document,
|
|
69592
|
-
const safeRoot = root || document;
|
|
69973
|
+
const { document, helpers, rule } = ctx;
|
|
69593
69974
|
|
|
69594
69975
|
function normalizeWs(s) {
|
|
69595
69976
|
return String(s || '').replace(/\s+/g, ' ').trim();
|
|
69596
69977
|
}
|
|
69597
69978
|
|
|
69598
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('table[summary]'
|
|
69979
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('table[summary]') : helpers.queryAll('table[summary]');
|
|
69599
69980
|
|
|
69600
69981
|
const occurrences = [];
|
|
69601
69982
|
let applicableCount = 0;
|
|
@@ -69641,12 +70022,11 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
69641
70022
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
69642
70023
|
}), applicability: null },
|
|
69643
70024
|
"table-fake-caption": { run: (function runInPage(ctx) {
|
|
69644
|
-
const { document,
|
|
69645
|
-
const safeRoot = root || document;
|
|
70025
|
+
const { document, helpers, rule } = ctx;
|
|
69646
70026
|
|
|
69647
70027
|
function trim(v) { return (v == null ? '' : String(v)).trim(); }
|
|
69648
70028
|
|
|
69649
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('table'
|
|
70029
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('table') : helpers.queryAll('table');
|
|
69650
70030
|
|
|
69651
70031
|
const occurrences = [];
|
|
69652
70032
|
let applicableCount = 0;
|
|
@@ -69709,10 +70089,9 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
69709
70089
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
69710
70090
|
}), applicability: null },
|
|
69711
70091
|
"table-headers-attr-valid": { run: (function runInPage(ctx) {
|
|
69712
|
-
const { document,
|
|
69713
|
-
const safeRoot = root || document;
|
|
70092
|
+
const { document, helpers, rule } = ctx;
|
|
69714
70093
|
|
|
69715
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('td[headers], th[headers]'
|
|
70094
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('td[headers], th[headers]') : helpers.queryAll('td[headers], th[headers]');
|
|
69716
70095
|
|
|
69717
70096
|
const occurrences = [];
|
|
69718
70097
|
let applicableCount = 0;
|
|
@@ -69786,10 +70165,9 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
69786
70165
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
69787
70166
|
}), applicability: null },
|
|
69788
70167
|
"table-th-has-data-cells": { run: (function runInPage(ctx) {
|
|
69789
|
-
const { document,
|
|
69790
|
-
const safeRoot = root || document;
|
|
70168
|
+
const { document, helpers, rule } = ctx;
|
|
69791
70169
|
|
|
69792
|
-
const tables = helpers.queryAllSmart ? helpers.queryAllSmart('table'
|
|
70170
|
+
const tables = helpers.queryAllSmart ? helpers.queryAllSmart('table') : helpers.queryAll('table');
|
|
69793
70171
|
|
|
69794
70172
|
const occurrences = [];
|
|
69795
70173
|
let applicableCount = 0;
|
|
@@ -70348,14 +70726,13 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
70348
70726
|
return { ruleId: RULE_ID, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
70349
70727
|
}), applicability: null },
|
|
70350
70728
|
"td-has-header": { run: (function runInPage(ctx) {
|
|
70351
|
-
const { document,
|
|
70352
|
-
const safeRoot = root || document;
|
|
70729
|
+
const { document, helpers, rule } = ctx;
|
|
70353
70730
|
|
|
70354
70731
|
const MIN_SIZE = 4;
|
|
70355
70732
|
|
|
70356
70733
|
function trim(v) { return (v == null ? '' : String(v)).trim(); }
|
|
70357
70734
|
|
|
70358
|
-
const tables = helpers.queryAllSmart ? helpers.queryAllSmart('table'
|
|
70735
|
+
const tables = helpers.queryAllSmart ? helpers.queryAllSmart('table') : helpers.queryAll('table');
|
|
70359
70736
|
|
|
70360
70737
|
const occurrences = [];
|
|
70361
70738
|
let applicableCount = 0;
|
|
@@ -70441,11 +70818,10 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
70441
70818
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
70442
70819
|
}), applicability: null },
|
|
70443
70820
|
"textbox-name-present": { run: (function runInPage(ctx) {
|
|
70444
|
-
const { document,
|
|
70821
|
+
const { document, helpers, rule } = ctx;
|
|
70445
70822
|
const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
|
|
70446
70823
|
? helpers.getEligibilityInfo
|
|
70447
70824
|
: null;
|
|
70448
|
-
const safeRoot = root || document;
|
|
70449
70825
|
|
|
70450
70826
|
|
|
70451
70827
|
function normalizeWs(s) {
|
|
@@ -70550,7 +70926,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
70550
70926
|
let applicableCount = 0;
|
|
70551
70927
|
|
|
70552
70928
|
const selector = "[role=\"textbox\"]";
|
|
70553
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
70929
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
70554
70930
|
|
|
70555
70931
|
// Precompute label[for] map for textbox elements that are labelable
|
|
70556
70932
|
// native form controls (e.g. <input role="textbox">).
|
|
@@ -70651,11 +71027,10 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
70651
71027
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
70652
71028
|
}), applicability: null },
|
|
70653
71029
|
"tooltip-name-present": { run: (function runInPage(ctx) {
|
|
70654
|
-
const { document,
|
|
71030
|
+
const { document, helpers, rule } = ctx;
|
|
70655
71031
|
const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
|
|
70656
71032
|
? helpers.getEligibilityInfo
|
|
70657
71033
|
: null;
|
|
70658
|
-
const safeRoot = root || document;
|
|
70659
71034
|
|
|
70660
71035
|
|
|
70661
71036
|
function normalizeWs(s) {
|
|
@@ -70719,7 +71094,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
70719
71094
|
let applicableCount = 0;
|
|
70720
71095
|
|
|
70721
71096
|
const selector = '[role="tooltip"]';
|
|
70722
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
71097
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
70723
71098
|
|
|
70724
71099
|
|
|
70725
71100
|
function evaluate(el) {
|
|
@@ -70783,11 +71158,10 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
70783
71158
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
70784
71159
|
}), applicability: null },
|
|
70785
71160
|
"treeitem-name-present": { run: (function runInPage(ctx) {
|
|
70786
|
-
const { document,
|
|
71161
|
+
const { document, helpers, rule } = ctx;
|
|
70787
71162
|
const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
|
|
70788
71163
|
? helpers.getEligibilityInfo
|
|
70789
71164
|
: null;
|
|
70790
|
-
const safeRoot = root || document;
|
|
70791
71165
|
|
|
70792
71166
|
|
|
70793
71167
|
function normalizeWs(s) {
|
|
@@ -70851,7 +71225,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
70851
71225
|
let applicableCount = 0;
|
|
70852
71226
|
|
|
70853
71227
|
const selector = "[role=\"treeitem\"]";
|
|
70854
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
71228
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
70855
71229
|
|
|
70856
71230
|
|
|
70857
71231
|
function hasName(el) {
|
|
@@ -70914,12 +71288,11 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
70914
71288
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
70915
71289
|
}), applicability: null },
|
|
70916
71290
|
"valid-lang": { run: (function runInPage(ctx) {
|
|
70917
|
-
const { document,
|
|
70918
|
-
const safeRoot = root || document;
|
|
71291
|
+
const { document, helpers, rule } = ctx;
|
|
70919
71292
|
|
|
70920
71293
|
const BCP47_RE = /^[a-zA-Z]{2,3}(-[a-zA-Z0-9]{2,8})*$/;
|
|
70921
71294
|
|
|
70922
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[lang]'
|
|
71295
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[lang]') : helpers.queryAll('[lang]');
|
|
70923
71296
|
|
|
70924
71297
|
const occurrences = [];
|
|
70925
71298
|
let applicableCount = 0;
|
|
@@ -70964,10 +71337,9 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
70964
71337
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
70965
71338
|
}), applicability: null },
|
|
70966
71339
|
"video-caption": { run: (function runInPage(ctx) {
|
|
70967
|
-
const { document,
|
|
70968
|
-
const safeRoot = root || document;
|
|
71340
|
+
const { document, helpers, rule } = ctx;
|
|
70969
71341
|
|
|
70970
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('video'
|
|
71342
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('video') : helpers.queryAll('video');
|
|
70971
71343
|
|
|
70972
71344
|
const occurrences = [];
|
|
70973
71345
|
let applicableCount = 0;
|
|
@@ -71689,10 +72061,12 @@ const I18N = {
|
|
|
71689
72061
|
"region_description": "Checks that direct children of <body> with visible text content are contained within a landmark region.",
|
|
71690
72062
|
"region_summary_cantTell": "This content is not contained within a landmark region.",
|
|
71691
72063
|
"region_hint_cantTell": "Move this content inside a landmark region (main, nav, aside, a labeled section, etc.).",
|
|
71692
|
-
"skipLink_title": "Skip link must have a resolvable target",
|
|
71693
|
-
"skipLink_description": "Checks that a \"skip to ...\" link's href fragment resolves to a real element in the document.",
|
|
72064
|
+
"skipLink_title": "Skip link must have a resolvable, usable target",
|
|
72065
|
+
"skipLink_description": "Checks that a \"skip to ...\" link's href fragment resolves to a real, currently usable element in the document.",
|
|
71694
72066
|
"skipLink_summary_cantTell": "This skip link's target does not exist.",
|
|
71695
72067
|
"skipLink_hint_cantTell": "Point the skip link's href at an id that exists in the document, or add the missing target element.",
|
|
72068
|
+
"skipLink_summary_unusableTarget_cantTell": "This skip link points to a target that exists but is not currently usable.",
|
|
72069
|
+
"skipLink_hint_unusableTarget_cantTell": "Point this skip link to a target that is exposed and usable as a navigation destination.",
|
|
71696
72070
|
"autocompleteValid_title": "autocomplete attribute must be a valid autofill value",
|
|
71697
72071
|
"autocompleteValid_description": "Checks that a non-empty autocomplete attribute is \"on\"/\"off\" or a well-formed autofill detail token list.",
|
|
71698
72072
|
"autocompleteValid_summary_fail": "This autocomplete attribute value is not a valid autofill value.",
|
|
@@ -72291,10 +72665,12 @@ const I18N = {
|
|
|
72291
72665
|
"region_description": "Vérifie que les enfants directs de <body> ayant un contenu textuel visible sont contenus dans un point de repère.",
|
|
72292
72666
|
"region_summary_cantTell": "Ce contenu n’est contenu dans aucun point de repère.",
|
|
72293
72667
|
"region_hint_cantTell": "Déplacez ce contenu à l’intérieur d’un point de repère (main, nav, aside, une section nommée, etc.).",
|
|
72294
|
-
"skipLink_title": "Un lien d’évitement doit avoir une cible qui se résout",
|
|
72295
|
-
"skipLink_description": "Vérifie que le fragment href d’un lien « aller au... » se résout vers un élément réel du document.",
|
|
72668
|
+
"skipLink_title": "Un lien d’évitement doit avoir une cible qui se résout et qui est utilisable",
|
|
72669
|
+
"skipLink_description": "Vérifie que le fragment href d’un lien « aller au... » se résout vers un élément réel et actuellement utilisable du document.",
|
|
72296
72670
|
"skipLink_summary_cantTell": "La cible de ce lien d’évitement n’existe pas.",
|
|
72297
72671
|
"skipLink_hint_cantTell": "Faites pointer le href du lien d’évitement vers un id qui existe dans le document, ou ajoutez l’élément cible manquant.",
|
|
72672
|
+
"skipLink_summary_unusableTarget_cantTell": "Ce lien d’évitement pointe vers une cible qui existe mais qui n’est pas actuellement utilisable.",
|
|
72673
|
+
"skipLink_hint_unusableTarget_cantTell": "Faites pointer ce lien d’évitement vers une cible exposée et utilisable comme destination de navigation.",
|
|
72298
72674
|
"autocompleteValid_title": "L’attribut autocomplete doit être une valeur d’auto-remplissage valide",
|
|
72299
72675
|
"autocompleteValid_description": "Vérifie qu’un attribut autocomplete non vide vaut « on »/« off » ou une liste de jetons d’auto-remplissage bien formée.",
|
|
72300
72676
|
"autocompleteValid_summary_fail": "Cette valeur d’attribut autocomplete n’est pas une valeur d’auto-remplissage valide.",
|
|
@@ -75100,6 +75476,10 @@ const createDomHelpers = (function createDomHelpers(opts) {
|
|
|
75100
75476
|
})();
|
|
75101
75477
|
// Default on: opt OUT with `includeShadowDom: false`, not opt in.
|
|
75102
75478
|
const includeShadowDom = !(opts && opts.includeShadowDom === false);
|
|
75479
|
+
// Default off: by default, helper queries skip structurally/CSS-hidden
|
|
75480
|
+
// subtrees (display:none, [hidden], closed <details>, etc.). Callers can
|
|
75481
|
+
// opt out with includeHiddenElements:true.
|
|
75482
|
+
const includeHiddenElements = !!(opts && opts.includeHiddenElements === true);
|
|
75103
75483
|
const excludeSelectors = Array.isArray(opts && opts.excludeSelectors) ? opts.excludeSelectors : [];
|
|
75104
75484
|
|
|
75105
75485
|
// Rule-scoped excludes (engineOptions.rules[ruleId].excludeSelectors), set
|
|
@@ -76066,8 +76446,55 @@ const createDomHelpers = (function createDomHelpers(opts) {
|
|
|
76066
76446
|
return results;
|
|
76067
76447
|
}
|
|
76068
76448
|
|
|
76449
|
+
const HARD_HIDDEN_REASONS = new Set([
|
|
76450
|
+
'displayNone',
|
|
76451
|
+
'hiddenAttr',
|
|
76452
|
+
'detailsClosed',
|
|
76453
|
+
'templateContent',
|
|
76454
|
+
'nonRenderedElement',
|
|
76455
|
+
'inputHidden',
|
|
76456
|
+
'visibilityHidden',
|
|
76457
|
+
'contentVisibilityHidden'
|
|
76458
|
+
]);
|
|
76459
|
+
|
|
76069
76460
|
function queryAllSmart(sel) {
|
|
76070
|
-
|
|
76461
|
+
let list = includeShadowDom ? queryAllDeep(sel) : queryAll(sel);
|
|
76462
|
+
|
|
76463
|
+
// Global hidden-content policy: skip nodes that are fully excluded from
|
|
76464
|
+
// rendered visibility by default (unless includeHiddenElements:true).
|
|
76465
|
+
if (!includeHiddenElements) {
|
|
76466
|
+
list = list.filter((el) => {
|
|
76467
|
+
try {
|
|
76468
|
+
const vis = isAccTreeEligible(el);
|
|
76469
|
+
if (!vis || vis.eligible !== false) return true;
|
|
76470
|
+
const reasons = Array.isArray(vis.reasons) ? vis.reasons : [];
|
|
76471
|
+
for (const r of reasons) {
|
|
76472
|
+
if (HARD_HIDDEN_REASONS.has(r)) return false;
|
|
76473
|
+
}
|
|
76474
|
+
|
|
76475
|
+
// `isAccTreeEligible` can short-circuit on an inert ancestor
|
|
76476
|
+
// before it reaches an outer hard-hidden ancestor (e.g.
|
|
76477
|
+
// display:none wrapper). In that case the node is still
|
|
76478
|
+
// structurally hidden and should be excluded by the default
|
|
76479
|
+
// hidden-content policy.
|
|
76480
|
+
if (reasons.includes('inert')) {
|
|
76481
|
+
const domVis = isDomVisibleEligible(el, null, {
|
|
76482
|
+
visibilityMode: 'styleOnly',
|
|
76483
|
+
disableGeometry: true,
|
|
76484
|
+
ignoreOpacity: true
|
|
76485
|
+
});
|
|
76486
|
+
const domReasons = Array.isArray(domVis && domVis.reasons) ? domVis.reasons : [];
|
|
76487
|
+
for (const r of domReasons) {
|
|
76488
|
+
if (HARD_HIDDEN_REASONS.has(r)) return false;
|
|
76489
|
+
}
|
|
76490
|
+
}
|
|
76491
|
+
return true;
|
|
76492
|
+
} catch {
|
|
76493
|
+
return true;
|
|
76494
|
+
}
|
|
76495
|
+
});
|
|
76496
|
+
}
|
|
76497
|
+
|
|
76071
76498
|
return __getEffectiveExcludeSelectors().length ? list.filter((el) => !isExcluded(el)) : list;
|
|
76072
76499
|
}
|
|
76073
76500
|
|
|
@@ -78637,6 +79064,36 @@ const createDomHelpers = (function createDomHelpers(opts) {
|
|
|
78637
79064
|
let node = el;
|
|
78638
79065
|
let safety = 0;
|
|
78639
79066
|
|
|
79067
|
+
// Only apply the "stop climbing once we reach a contextSelector-
|
|
79068
|
+
// matched root" shortcut when there's a single (or no) matched
|
|
79069
|
+
// root -- resolveContextRoots() falls back to `[documentElement]`
|
|
79070
|
+
// when no contextSelector is given, so this is the overwhelmingly
|
|
79071
|
+
// common case and behaves exactly as before.
|
|
79072
|
+
//
|
|
79073
|
+
// With MULTIPLE matched roots (multi-region contextSelector
|
|
79074
|
+
// scans), stopping there without recording anything about which
|
|
79075
|
+
// root produced an ambiguous, non-unique selector string for two
|
|
79076
|
+
// structurally-identical regions -- a real, confirmed bug (found
|
|
79077
|
+
// 2026-07-29 via the cross-engine comparisons project): two
|
|
79078
|
+
// wrapper <div>s, each containing two identical ".widget"
|
|
79079
|
+
// sections scanned via `contextSelector: '.widget'`, produced the
|
|
79080
|
+
// *same* selector string ("section:nth-of-type(1) > div > div >
|
|
79081
|
+
// button") for the equivalent button in each wrapper --
|
|
79082
|
+
// resolving to 2 elements instead of 1 when queried, and pointing
|
|
79083
|
+
// at the wrong one for at least one of the two occurrences. The
|
|
79084
|
+
// existing `el.matches(candidate)` safety check below couldn't
|
|
79085
|
+
// catch this: it only verifies THIS element matches the string,
|
|
79086
|
+
// never that the string is unique document-wide.
|
|
79087
|
+
//
|
|
79088
|
+
// Fix: when multiple roots are in play, don't stop early --
|
|
79089
|
+
// keep climbing (same as the always-correct no-contextSelector
|
|
79090
|
+
// path) until finding a genuinely unique anchor or reaching the
|
|
79091
|
+
// true document root, which is always singular. That restores
|
|
79092
|
+
// the invariant the final safety-check comment below relies on,
|
|
79093
|
+
// rather than needing a separate (more expensive) document-wide
|
|
79094
|
+
// uniqueness re-check.
|
|
79095
|
+
const stopAtMatchedRoot = roots.length <= 1;
|
|
79096
|
+
|
|
78640
79097
|
while (node && node.nodeType === 1 && safety++ < 20) {
|
|
78641
79098
|
let anchor = null;
|
|
78642
79099
|
|
|
@@ -78676,7 +79133,7 @@ const createDomHelpers = (function createDomHelpers(opts) {
|
|
|
78676
79133
|
parts.unshift(nthOfType(node));
|
|
78677
79134
|
}
|
|
78678
79135
|
|
|
78679
|
-
if (!node.parentElement || roots.includes(node)) break;
|
|
79136
|
+
if (!node.parentElement || (stopAtMatchedRoot && roots.includes(node))) break;
|
|
78680
79137
|
node = node.parentElement;
|
|
78681
79138
|
}
|
|
78682
79139
|
|
|
@@ -78695,7 +79152,12 @@ const createDomHelpers = (function createDomHelpers(opts) {
|
|
|
78695
79152
|
// position relative to its own parent via `>` (child, not
|
|
78696
79153
|
// descendant) combinators, so a correctly-matching chain can
|
|
78697
79154
|
// only resolve to one element short of a malformed document
|
|
78698
|
-
// (e.g. two <html> roots)
|
|
79155
|
+
// (e.g. two <html> roots) -- true as long as the walk above
|
|
79156
|
+
// never stops short of a genuinely unique anchor/root, which is
|
|
79157
|
+
// exactly what `stopAtMatchedRoot` now guarantees (see its own
|
|
79158
|
+
// comment above; a multi-root contextSelector scan stopping
|
|
79159
|
+
// early used to violate this invariant silently). Re-deriving
|
|
79160
|
+
// that guarantee via a
|
|
78699
79161
|
// document-wide :nth-of-type scan was measured to cost O(total
|
|
78700
79162
|
// same-tag siblings) per call — pathological on pages with many
|
|
78701
79163
|
// flat, unidentified siblings (e.g. hundreds of unlabeled
|
|
@@ -79262,6 +79724,9 @@ const runCore = (function runCore(pageUrl, contextSelector, engineOptions, runOn
|
|
|
79262
79724
|
|
|
79263
79725
|
// Default on: opt OUT with `includeShadowDom: false`, not opt in.
|
|
79264
79726
|
const includeShadowDom = !(engineOptionsResolved && engineOptionsResolved.includeShadowDom === false);
|
|
79727
|
+
// Default off: hidden/collapsed content is excluded from rule evaluation
|
|
79728
|
+
// unless the caller explicitly opts in.
|
|
79729
|
+
const includeHiddenElements = !!(engineOptionsResolved && engineOptionsResolved.includeHiddenElements === true);
|
|
79265
79730
|
const excludeSelectors = normalizeSelectorList(engineOptionsResolved && engineOptionsResolved.excludeSelectors);
|
|
79266
79731
|
|
|
79267
79732
|
const url = pageUrl || (document.location && document.location.href) || null;
|
|
@@ -79272,11 +79737,27 @@ const runCore = (function runCore(pageUrl, contextSelector, engineOptions, runOn
|
|
|
79272
79737
|
? engineOptionsResolved.timestamp.trim()
|
|
79273
79738
|
: null;
|
|
79274
79739
|
|
|
79740
|
+
// createDomHelpers()/createContrastHelpers() persist their element-keyed
|
|
79741
|
+
// caches (outerHtmlCache, selectorCache, etc.) on window.__a11ycoreSharedCache
|
|
79742
|
+
// so multiple helper instances created *within this run* can share them
|
|
79743
|
+
// deterministically. But a window/document is frequently reused across
|
|
79744
|
+
// SEPARATE runs -- e.g. Jest's jsdom environment creates one window per
|
|
79745
|
+
// test file, and mutating document.body between it() blocks is standard.
|
|
79746
|
+
// Those caches are keyed by element reference, not content, so a run that
|
|
79747
|
+
// reuses an already-cached element (document.body never changes identity)
|
|
79748
|
+
// would otherwise read stale data cached by an earlier, unrelated run on
|
|
79749
|
+
// the same window. Clearing at the start of every run keeps sharing scoped
|
|
79750
|
+
// to "this run" as intended, without leaking across runs.
|
|
79751
|
+
try {
|
|
79752
|
+
if (window && window.__a11ycoreSharedCache) window.__a11ycoreSharedCache = {};
|
|
79753
|
+
} catch {}
|
|
79754
|
+
|
|
79275
79755
|
const sharedHelpers = createDomHelpers({
|
|
79276
79756
|
document,
|
|
79277
79757
|
window,
|
|
79278
79758
|
root: roots,
|
|
79279
79759
|
includeShadowDom,
|
|
79760
|
+
includeHiddenElements,
|
|
79280
79761
|
excludeSelectors,
|
|
79281
79762
|
// Optional perf counters (bench/debug only). Deterministic and per-run.
|
|
79282
79763
|
perfStats: !!(engineOptionsResolved && engineOptionsResolved.perfStats)
|