@surea11y/core 1.1.1 → 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 +15 -0
- package/package.json +1 -1
- package/src/checks/automatic/aria-allowed-attr.js +2 -3
- package/src/checks/automatic/aria-allowed-role.js +2 -3
- 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 +2 -3
- 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 +2 -3
- package/src/checks/automatic/aria-valid-attr.js +2 -3
- 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 +2 -3
- 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 +2 -3
- 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 +2 -3
- package/src/checks/automatic/table-th-has-data-cells.js +2 -3
- 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 +3 -4
- 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 +56 -3
- package/src/core/dom-runner.js +15 -0
- package/src/core.js +851 -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.",
|
|
@@ -11944,7 +11948,8 @@ const createDomHelpers = (function createDomHelpers(opts) {
|
|
|
11944
11948
|
'templateContent',
|
|
11945
11949
|
'nonRenderedElement',
|
|
11946
11950
|
'inputHidden',
|
|
11947
|
-
'visibilityHidden'
|
|
11951
|
+
'visibilityHidden',
|
|
11952
|
+
'contentVisibilityHidden'
|
|
11948
11953
|
]);
|
|
11949
11954
|
|
|
11950
11955
|
function queryAllSmart(sel) {
|
|
@@ -11961,6 +11966,23 @@ const createDomHelpers = (function createDomHelpers(opts) {
|
|
|
11961
11966
|
for (const r of reasons) {
|
|
11962
11967
|
if (HARD_HIDDEN_REASONS.has(r)) return false;
|
|
11963
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
|
+
}
|
|
11964
11986
|
return true;
|
|
11965
11987
|
} catch {
|
|
11966
11988
|
return true;
|
|
@@ -14537,6 +14559,36 @@ const createDomHelpers = (function createDomHelpers(opts) {
|
|
|
14537
14559
|
let node = el;
|
|
14538
14560
|
let safety = 0;
|
|
14539
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
|
+
|
|
14540
14592
|
while (node && node.nodeType === 1 && safety++ < 20) {
|
|
14541
14593
|
let anchor = null;
|
|
14542
14594
|
|
|
@@ -14576,7 +14628,7 @@ const createDomHelpers = (function createDomHelpers(opts) {
|
|
|
14576
14628
|
parts.unshift(nthOfType(node));
|
|
14577
14629
|
}
|
|
14578
14630
|
|
|
14579
|
-
if (!node.parentElement || roots.includes(node)) break;
|
|
14631
|
+
if (!node.parentElement || (stopAtMatchedRoot && roots.includes(node))) break;
|
|
14580
14632
|
node = node.parentElement;
|
|
14581
14633
|
}
|
|
14582
14634
|
|
|
@@ -14595,7 +14647,12 @@ const createDomHelpers = (function createDomHelpers(opts) {
|
|
|
14595
14647
|
// position relative to its own parent via `>` (child, not
|
|
14596
14648
|
// descendant) combinators, so a correctly-matching chain can
|
|
14597
14649
|
// only resolve to one element short of a malformed document
|
|
14598
|
-
// (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
|
|
14599
14656
|
// document-wide :nth-of-type scan was measured to cost O(total
|
|
14600
14657
|
// same-tag siblings) per call — pathological on pages with many
|
|
14601
14658
|
// flat, unidentified siblings (e.g. hundreds of unlabeled
|
|
@@ -15175,6 +15232,21 @@ const runCore = (function runCore(pageUrl, contextSelector, engineOptions, runOn
|
|
|
15175
15232
|
? engineOptionsResolved.timestamp.trim()
|
|
15176
15233
|
: null;
|
|
15177
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
|
+
|
|
15178
15250
|
const sharedHelpers = createDomHelpers({
|
|
15179
15251
|
document,
|
|
15180
15252
|
window,
|
|
@@ -21284,8 +21356,8 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
21284
21356
|
},
|
|
21285
21357
|
{
|
|
21286
21358
|
"ruleId": "skip-link",
|
|
21287
|
-
"title": "Skip link must have a resolvable target",
|
|
21288
|
-
"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.",
|
|
21289
21361
|
"i18n": {
|
|
21290
21362
|
"titleKey": "skipLink_title",
|
|
21291
21363
|
"descriptionKey": "skipLink_description"
|
|
@@ -22877,13 +22949,7 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
22877
22949
|
const RULE_IMPLS = {
|
|
22878
22950
|
"accesskeys": { run: (function runInPage(ctx) {
|
|
22879
22951
|
const { document, helpers, rule } = ctx;
|
|
22880
|
-
|
|
22881
|
-
let nodes = [];
|
|
22882
|
-
try {
|
|
22883
|
-
nodes = document.querySelectorAll('[accesskey]');
|
|
22884
|
-
} catch {
|
|
22885
|
-
nodes = [];
|
|
22886
|
-
}
|
|
22952
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[accesskey]') : helpers.queryAll('[accesskey]');
|
|
22887
22953
|
|
|
22888
22954
|
const groups = new Map(); // normalized key -> elements[]
|
|
22889
22955
|
for (const el of nodes) {
|
|
@@ -23474,8 +23540,7 @@ if (isAccTreeEligible) {
|
|
|
23474
23540
|
return { ruleId: rule.ruleId, outcome: 'cantTell', severity: 'minor', occurrences };
|
|
23475
23541
|
}), applicability: null },
|
|
23476
23542
|
"aria-allowed-attr": { run: (function runInPage(ctx) {
|
|
23477
|
-
const { document,
|
|
23478
|
-
const safeRoot = root || document;
|
|
23543
|
+
const { document, helpers, rule } = ctx;
|
|
23479
23544
|
|
|
23480
23545
|
const ariaHelpers = helpers && helpers.aria ? helpers.aria : null;
|
|
23481
23546
|
if (!ariaHelpers) {
|
|
@@ -23539,7 +23604,7 @@ if (isAccTreeEligible) {
|
|
|
23539
23604
|
};
|
|
23540
23605
|
|
|
23541
23606
|
const globalSet = new Set(GLOBAL_ATTRS);
|
|
23542
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role]'
|
|
23607
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role]') : helpers.queryAll('[role]');
|
|
23543
23608
|
|
|
23544
23609
|
const occurrences = [];
|
|
23545
23610
|
let applicableCount = 0;
|
|
@@ -23604,15 +23669,14 @@ if (isAccTreeEligible) {
|
|
|
23604
23669
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
23605
23670
|
}), applicability: null },
|
|
23606
23671
|
"aria-allowed-role": { run: (function runInPage(ctx) {
|
|
23607
|
-
const { document,
|
|
23608
|
-
const safeRoot = root || document;
|
|
23672
|
+
const { document, helpers, rule } = ctx;
|
|
23609
23673
|
|
|
23610
23674
|
const ariaHelpers = helpers && helpers.aria ? helpers.aria : null;
|
|
23611
23675
|
if (!ariaHelpers) {
|
|
23612
23676
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
23613
23677
|
}
|
|
23614
23678
|
|
|
23615
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role]'
|
|
23679
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role]') : helpers.queryAll('[role]');
|
|
23616
23680
|
|
|
23617
23681
|
const occurrences = [];
|
|
23618
23682
|
let applicableCount = 0;
|
|
@@ -23659,8 +23723,7 @@ if (isAccTreeEligible) {
|
|
|
23659
23723
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
23660
23724
|
}), applicability: null },
|
|
23661
23725
|
"aria-braille-equivalent": { run: (function runInPage(ctx) {
|
|
23662
|
-
const { document,
|
|
23663
|
-
const safeRoot = root || document;
|
|
23726
|
+
const { document, helpers, rule } = ctx;
|
|
23664
23727
|
|
|
23665
23728
|
function trim(v) { return (v == null ? '' : String(v)).trim(); }
|
|
23666
23729
|
|
|
@@ -23681,8 +23744,8 @@ if (isAccTreeEligible) {
|
|
|
23681
23744
|
}
|
|
23682
23745
|
|
|
23683
23746
|
const nodes = helpers.queryAllSmart
|
|
23684
|
-
? helpers.queryAllSmart('[aria-braillelabel], [aria-brailleroledescription]'
|
|
23685
|
-
: helpers.queryAll('[aria-braillelabel], [aria-brailleroledescription]'
|
|
23747
|
+
? helpers.queryAllSmart('[aria-braillelabel], [aria-brailleroledescription]')
|
|
23748
|
+
: helpers.queryAll('[aria-braillelabel], [aria-brailleroledescription]');
|
|
23686
23749
|
|
|
23687
23750
|
const occurrences = [];
|
|
23688
23751
|
let applicableCount = 0;
|
|
@@ -23743,14 +23806,13 @@ if (isAccTreeEligible) {
|
|
|
23743
23806
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
23744
23807
|
}), applicability: null },
|
|
23745
23808
|
"aria-checked-state-mismatch": { run: (function runInPage(ctx) {
|
|
23746
|
-
const { document,
|
|
23747
|
-
const safeRoot = root || document;
|
|
23809
|
+
const { document, helpers, rule } = ctx;
|
|
23748
23810
|
|
|
23749
23811
|
function trim(v) { return (v == null ? '' : String(v)).trim(); }
|
|
23750
23812
|
|
|
23751
23813
|
const nodes = helpers.queryAllSmart
|
|
23752
|
-
? helpers.queryAllSmart('input[type="checkbox"][aria-checked], input[type="radio"][aria-checked]'
|
|
23753
|
-
: 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]');
|
|
23754
23816
|
|
|
23755
23817
|
const occurrences = [];
|
|
23756
23818
|
let applicableCount = 0;
|
|
@@ -23812,16 +23874,15 @@ if (isAccTreeEligible) {
|
|
|
23812
23874
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
23813
23875
|
}), applicability: null },
|
|
23814
23876
|
"aria-conditional-attr": { run: (function runInPage(ctx) {
|
|
23815
|
-
const { document,
|
|
23816
|
-
const safeRoot = root || document;
|
|
23877
|
+
const { document, helpers, rule } = ctx;
|
|
23817
23878
|
|
|
23818
23879
|
function trim(v) { return (v == null ? '' : String(v)).trim(); }
|
|
23819
23880
|
|
|
23820
23881
|
const TRUTHY_INVALID_VALUES = new Set(['true', 'grammar', 'spelling']);
|
|
23821
23882
|
|
|
23822
23883
|
const nodes = helpers.queryAllSmart
|
|
23823
|
-
? helpers.queryAllSmart('[aria-errormessage]'
|
|
23824
|
-
: helpers.queryAll('[aria-errormessage]'
|
|
23884
|
+
? helpers.queryAllSmart('[aria-errormessage]')
|
|
23885
|
+
: helpers.queryAll('[aria-errormessage]');
|
|
23825
23886
|
|
|
23826
23887
|
const occurrences = [];
|
|
23827
23888
|
let applicableCount = 0;
|
|
@@ -23866,15 +23927,14 @@ if (isAccTreeEligible) {
|
|
|
23866
23927
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
23867
23928
|
}), applicability: null },
|
|
23868
23929
|
"aria-deprecated-role": { run: (function runInPage(ctx) {
|
|
23869
|
-
const { document,
|
|
23870
|
-
const safeRoot = root || document;
|
|
23930
|
+
const { document, helpers, rule } = ctx;
|
|
23871
23931
|
|
|
23872
23932
|
const ariaHelpers = helpers && helpers.aria ? helpers.aria : null;
|
|
23873
23933
|
if (!ariaHelpers) {
|
|
23874
23934
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
23875
23935
|
}
|
|
23876
23936
|
|
|
23877
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role]'
|
|
23937
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role]') : helpers.queryAll('[role]');
|
|
23878
23938
|
|
|
23879
23939
|
const occurrences = [];
|
|
23880
23940
|
let applicableCount = 0;
|
|
@@ -24386,8 +24446,7 @@ if (isAccTreeEligible) {
|
|
|
24386
24446
|
return { ruleId: rule.ruleId, outcome: 'fail', severity: rule.defaultSeverity || 'minor', occurrences };
|
|
24387
24447
|
}), applicability: null },
|
|
24388
24448
|
"aria-prohibited-attr": { run: (function runInPage(ctx) {
|
|
24389
|
-
const { document,
|
|
24390
|
-
const safeRoot = root || document;
|
|
24449
|
+
const { document, helpers, rule } = ctx;
|
|
24391
24450
|
|
|
24392
24451
|
const ariaHelpers = helpers && helpers.aria ? helpers.aria : null;
|
|
24393
24452
|
if (!ariaHelpers) {
|
|
@@ -24407,7 +24466,7 @@ if (isAccTreeEligible) {
|
|
|
24407
24466
|
|
|
24408
24467
|
const PROHIBITED_NAMING_ATTRS = ['aria-label', 'aria-labelledby'];
|
|
24409
24468
|
|
|
24410
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role]'
|
|
24469
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role]') : helpers.queryAll('[role]');
|
|
24411
24470
|
|
|
24412
24471
|
const occurrences = [];
|
|
24413
24472
|
let applicableCount = 0;
|
|
@@ -24458,8 +24517,7 @@ if (isAccTreeEligible) {
|
|
|
24458
24517
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
24459
24518
|
}), applicability: null },
|
|
24460
24519
|
"aria-prohibited-children": { run: (function runInPage(ctx) {
|
|
24461
|
-
const { document,
|
|
24462
|
-
const safeRoot = root || document;
|
|
24520
|
+
const { document, helpers, rule } = ctx;
|
|
24463
24521
|
|
|
24464
24522
|
const ariaHelpers = helpers && helpers.aria ? helpers.aria : null;
|
|
24465
24523
|
if (!ariaHelpers) {
|
|
@@ -24548,7 +24606,7 @@ if (isAccTreeEligible) {
|
|
|
24548
24606
|
}
|
|
24549
24607
|
}
|
|
24550
24608
|
|
|
24551
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role]'
|
|
24609
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role]') : helpers.queryAll('[role]');
|
|
24552
24610
|
|
|
24553
24611
|
const occurrences = [];
|
|
24554
24612
|
let applicableCount = 0;
|
|
@@ -24620,8 +24678,7 @@ if (isAccTreeEligible) {
|
|
|
24620
24678
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
24621
24679
|
}), applicability: null },
|
|
24622
24680
|
"aria-required-attr": { run: (function runInPage(ctx) {
|
|
24623
|
-
const { document,
|
|
24624
|
-
const safeRoot = root || document;
|
|
24681
|
+
const { document, helpers, rule } = ctx;
|
|
24625
24682
|
|
|
24626
24683
|
const ariaHelpers = helpers && helpers.aria ? helpers.aria : null;
|
|
24627
24684
|
if (!ariaHelpers) {
|
|
@@ -24645,7 +24702,7 @@ if (isAccTreeEligible) {
|
|
|
24645
24702
|
return v != null && String(v).trim().toLowerCase() === 'true';
|
|
24646
24703
|
}
|
|
24647
24704
|
|
|
24648
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role]'
|
|
24705
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role]') : helpers.queryAll('[role]');
|
|
24649
24706
|
|
|
24650
24707
|
const occurrences = [];
|
|
24651
24708
|
let applicableCount = 0;
|
|
@@ -24702,8 +24759,7 @@ if (isAccTreeEligible) {
|
|
|
24702
24759
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
24703
24760
|
}), applicability: null },
|
|
24704
24761
|
"aria-required-children": { run: (function runInPage(ctx) {
|
|
24705
|
-
const { document,
|
|
24706
|
-
const safeRoot = root || document;
|
|
24762
|
+
const { document, helpers, rule } = ctx;
|
|
24707
24763
|
|
|
24708
24764
|
const ariaHelpers = helpers && helpers.aria ? helpers.aria : null;
|
|
24709
24765
|
if (!ariaHelpers) {
|
|
@@ -24734,7 +24790,7 @@ if (isAccTreeEligible) {
|
|
|
24734
24790
|
// ("runInPage MUST be self-contained").
|
|
24735
24791
|
const CANDIDATE_SELECTOR = '[role], li, option, tr, td, th, thead, tbody, tfoot, ul, ol, table, select, input[type="radio"]';
|
|
24736
24792
|
|
|
24737
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role]'
|
|
24793
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role]') : helpers.queryAll('[role]');
|
|
24738
24794
|
|
|
24739
24795
|
const occurrences = [];
|
|
24740
24796
|
let applicableCount = 0;
|
|
@@ -24893,8 +24949,7 @@ if (isAccTreeEligible) {
|
|
|
24893
24949
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
24894
24950
|
}), applicability: null },
|
|
24895
24951
|
"aria-required-parent": { run: (function runInPage(ctx) {
|
|
24896
|
-
const { document,
|
|
24897
|
-
const safeRoot = root || document;
|
|
24952
|
+
const { document, helpers, rule } = ctx;
|
|
24898
24953
|
|
|
24899
24954
|
const ariaHelpers = helpers && helpers.aria ? helpers.aria : null;
|
|
24900
24955
|
if (!ariaHelpers) {
|
|
@@ -24980,7 +25035,7 @@ if (isAccTreeEligible) {
|
|
|
24980
25035
|
const idTok = elId && String(elId).trim();
|
|
24981
25036
|
if (!idTok) return false;
|
|
24982
25037
|
|
|
24983
|
-
const owners = helpers.queryAllSmart ? helpers.queryAllSmart('[aria-owns]'
|
|
25038
|
+
const owners = helpers.queryAllSmart ? helpers.queryAllSmart('[aria-owns]') : helpers.queryAll('[aria-owns]');
|
|
24984
25039
|
for (const owner of owners) {
|
|
24985
25040
|
if (!owner || !owner.getAttribute) continue;
|
|
24986
25041
|
const ownsAttr = owner.getAttribute('aria-owns') || '';
|
|
@@ -24993,7 +25048,7 @@ if (isAccTreeEligible) {
|
|
|
24993
25048
|
return false;
|
|
24994
25049
|
}
|
|
24995
25050
|
|
|
24996
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role]'
|
|
25051
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role]') : helpers.queryAll('[role]');
|
|
24997
25052
|
|
|
24998
25053
|
const occurrences = [];
|
|
24999
25054
|
let applicableCount = 0;
|
|
@@ -25206,15 +25261,14 @@ if (isAccTreeEligible) {
|
|
|
25206
25261
|
return { ruleId: rule.ruleId, outcome: 'fail', severity: rule.defaultSeverity || 'minor', occurrences };
|
|
25207
25262
|
}), applicability: null },
|
|
25208
25263
|
"aria-roles-valid": { run: (function runInPage(ctx) {
|
|
25209
|
-
const { document,
|
|
25210
|
-
const safeRoot = root || document;
|
|
25264
|
+
const { document, helpers, rule } = ctx;
|
|
25211
25265
|
|
|
25212
25266
|
const ariaHelpers = helpers && helpers.aria ? helpers.aria : null;
|
|
25213
25267
|
if (!ariaHelpers) {
|
|
25214
25268
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
25215
25269
|
}
|
|
25216
25270
|
|
|
25217
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role]'
|
|
25271
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role]') : helpers.queryAll('[role]');
|
|
25218
25272
|
|
|
25219
25273
|
const occurrences = [];
|
|
25220
25274
|
let applicableCount = 0;
|
|
@@ -25269,13 +25323,12 @@ if (isAccTreeEligible) {
|
|
|
25269
25323
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
25270
25324
|
}), applicability: null },
|
|
25271
25325
|
"aria-text": { run: (function runInPage(ctx) {
|
|
25272
|
-
const { document,
|
|
25273
|
-
const safeRoot = root || document;
|
|
25326
|
+
const { document, helpers, rule } = ctx;
|
|
25274
25327
|
|
|
25275
25328
|
const FOCUSABLE_DESCENDANT_SELECTOR =
|
|
25276
25329
|
'a[href], button, input, select, textarea, [tabindex], iframe, [contenteditable]:not([contenteditable="false"])';
|
|
25277
25330
|
|
|
25278
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role="text"]'
|
|
25331
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role="text"]') : helpers.queryAll('[role="text"]');
|
|
25279
25332
|
|
|
25280
25333
|
const occurrences = [];
|
|
25281
25334
|
let applicableCount = 0;
|
|
@@ -25329,15 +25382,14 @@ if (isAccTreeEligible) {
|
|
|
25329
25382
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
25330
25383
|
}), applicability: null },
|
|
25331
25384
|
"aria-valid-attr": { run: (function runInPage(ctx) {
|
|
25332
|
-
const { document,
|
|
25333
|
-
const safeRoot = root || document;
|
|
25385
|
+
const { document, helpers, rule } = ctx;
|
|
25334
25386
|
|
|
25335
25387
|
const ariaHelpers = helpers && helpers.aria ? helpers.aria : null;
|
|
25336
25388
|
if (!ariaHelpers) {
|
|
25337
25389
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
25338
25390
|
}
|
|
25339
25391
|
|
|
25340
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('*'
|
|
25392
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('*') : helpers.queryAll('*');
|
|
25341
25393
|
|
|
25342
25394
|
const occurrences = [];
|
|
25343
25395
|
let applicableCount = 0;
|
|
@@ -25391,15 +25443,14 @@ if (isAccTreeEligible) {
|
|
|
25391
25443
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
25392
25444
|
}), applicability: null },
|
|
25393
25445
|
"aria-valid-attr-value": { run: (function runInPage(ctx) {
|
|
25394
|
-
const { document,
|
|
25395
|
-
const safeRoot = root || document;
|
|
25446
|
+
const { document, helpers, rule } = ctx;
|
|
25396
25447
|
|
|
25397
25448
|
const ariaHelpers = helpers && helpers.aria ? helpers.aria : null;
|
|
25398
25449
|
if (!ariaHelpers) {
|
|
25399
25450
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
25400
25451
|
}
|
|
25401
25452
|
|
|
25402
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('*'
|
|
25453
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('*') : helpers.queryAll('*');
|
|
25403
25454
|
|
|
25404
25455
|
const occurrences = [];
|
|
25405
25456
|
let applicableCount = 0;
|
|
@@ -25456,8 +25507,7 @@ if (isAccTreeEligible) {
|
|
|
25456
25507
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
25457
25508
|
}), applicability: null },
|
|
25458
25509
|
"autocomplete-valid": { run: (function runInPage(ctx) {
|
|
25459
|
-
const { document,
|
|
25460
|
-
const safeRoot = root || document;
|
|
25510
|
+
const { document, helpers, rule } = ctx;
|
|
25461
25511
|
|
|
25462
25512
|
// Declared inside runInPage — see scripts/build-core.js header
|
|
25463
25513
|
// ("runInPage MUST be self-contained").
|
|
@@ -25492,7 +25542,7 @@ if (isAccTreeEligible) {
|
|
|
25492
25542
|
return FIELD_NAMES.has(remaining[0]);
|
|
25493
25543
|
}
|
|
25494
25544
|
|
|
25495
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('input, select, textarea'
|
|
25545
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('input, select, textarea') : helpers.queryAll('input, select, textarea');
|
|
25496
25546
|
|
|
25497
25547
|
const occurrences = [];
|
|
25498
25548
|
let applicableCount = 0;
|
|
@@ -25535,12 +25585,11 @@ if (isAccTreeEligible) {
|
|
|
25535
25585
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
25536
25586
|
}), applicability: null },
|
|
25537
25587
|
"avoid-inline-spacing": { run: (function runInPage(ctx) {
|
|
25538
|
-
const { document,
|
|
25539
|
-
const safeRoot = root || document;
|
|
25588
|
+
const { document, helpers, rule } = ctx;
|
|
25540
25589
|
|
|
25541
25590
|
const SPACING_PROPS = ['line-height', 'letter-spacing', 'word-spacing'];
|
|
25542
25591
|
|
|
25543
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[style]'
|
|
25592
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[style]') : helpers.queryAll('[style]');
|
|
25544
25593
|
|
|
25545
25594
|
const occurrences = [];
|
|
25546
25595
|
let applicableCount = 0;
|
|
@@ -25593,11 +25642,10 @@ if (isAccTreeEligible) {
|
|
|
25593
25642
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
25594
25643
|
}), applicability: null },
|
|
25595
25644
|
"binary-control-name-present": { run: (function runInPage(ctx) {
|
|
25596
|
-
const { document,
|
|
25645
|
+
const { document, helpers, rule } = ctx;
|
|
25597
25646
|
const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
|
|
25598
25647
|
? helpers.getEligibilityInfo
|
|
25599
25648
|
: null;
|
|
25600
|
-
const safeRoot = root || document;
|
|
25601
25649
|
|
|
25602
25650
|
|
|
25603
25651
|
function normalizeWs(s) {
|
|
@@ -25729,7 +25777,7 @@ if (isAccTreeEligible) {
|
|
|
25729
25777
|
let applicableCount = 0;
|
|
25730
25778
|
|
|
25731
25779
|
const selector = 'input[type="checkbox"], input[type="radio"], [role="checkbox"], [role="radio"], [role="switch"]';
|
|
25732
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
25780
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
25733
25781
|
|
|
25734
25782
|
|
|
25735
25783
|
// Precompute label[for] associations once for speed/determinism.
|
|
@@ -25861,8 +25909,7 @@ if (isAccTreeEligible) {
|
|
|
25861
25909
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
25862
25910
|
}), applicability: null },
|
|
25863
25911
|
"button-name-present": { run: (function runInPage(ctx) {
|
|
25864
|
-
const { document,
|
|
25865
|
-
const safeRoot = root || document;
|
|
25912
|
+
const { document, helpers, rule } = ctx;
|
|
25866
25913
|
|
|
25867
25914
|
const occurrences = [];
|
|
25868
25915
|
let applicableCount = 0;
|
|
@@ -25899,7 +25946,7 @@ if (isAccTreeEligible) {
|
|
|
25899
25946
|
}
|
|
25900
25947
|
|
|
25901
25948
|
const selector = 'button, input[type="button"], input[type="submit"], input[type="reset"], [role="button"]';
|
|
25902
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
25949
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
25903
25950
|
|
|
25904
25951
|
for (const el of nodes) {
|
|
25905
25952
|
// isAccTreeEligible returns { eligible, reasons }, not a boolean.
|
|
@@ -25911,6 +25958,7 @@ if (isAccTreeEligible) {
|
|
|
25911
25958
|
|
|
25912
25959
|
const tag = (el.tagName || '').toLowerCase();
|
|
25913
25960
|
const role = el.getAttribute ? el.getAttribute('role') : null;
|
|
25961
|
+
const roleNorm = normalizeWs(role).toLowerCase();
|
|
25914
25962
|
|
|
25915
25963
|
const nameInfo = helpers.getAccessibleNameInfo ? helpers.getAccessibleNameInfo(el, ctx) : null;
|
|
25916
25964
|
|
|
@@ -25926,7 +25974,17 @@ if (isAccTreeEligible) {
|
|
|
25926
25974
|
inputValueName = getInputButtonValueName(el);
|
|
25927
25975
|
}
|
|
25928
25976
|
|
|
25929
|
-
|
|
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);
|
|
25930
25988
|
const contentName =
|
|
25931
25989
|
(!trustedProgrammaticName && !inputValueName && isContentNameCandidate)
|
|
25932
25990
|
? getConservativeSubtreeText(el)
|
|
@@ -25986,12 +26044,41 @@ if (isAccTreeEligible) {
|
|
|
25986
26044
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
25987
26045
|
}
|
|
25988
26046
|
|
|
25989
|
-
function
|
|
26047
|
+
const isAccTreeEligible = helpers && typeof helpers.isAccTreeEligible === 'function' ? helpers.isAccTreeEligible : null;
|
|
26048
|
+
|
|
26049
|
+
function isExposedToAt(el) {
|
|
26050
|
+
if (!isAccTreeEligible) return true;
|
|
25990
26051
|
try {
|
|
25991
|
-
|
|
26052
|
+
const r = isAccTreeEligible(el, ctx);
|
|
26053
|
+
if (typeof r === 'boolean') return r;
|
|
26054
|
+
return !!(r && r.eligible);
|
|
25992
26055
|
} catch {
|
|
25993
|
-
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;
|
|
25994
26080
|
}
|
|
26081
|
+
return false;
|
|
25995
26082
|
}
|
|
25996
26083
|
|
|
25997
26084
|
function hasWorkingAnchorLink() {
|
|
@@ -26033,11 +26120,10 @@ if (isAccTreeEligible) {
|
|
|
26033
26120
|
}
|
|
26034
26121
|
|
|
26035
26122
|
function hasHeading() {
|
|
26036
|
-
|
|
26037
|
-
|
|
26038
|
-
} catch {
|
|
26039
|
-
return false;
|
|
26123
|
+
for (const el of queryAll('h1, h2, h3, h4, h5, h6, [role="heading"]')) {
|
|
26124
|
+
if (el && isExposedToAt(el)) return true;
|
|
26040
26125
|
}
|
|
26126
|
+
return false;
|
|
26041
26127
|
}
|
|
26042
26128
|
|
|
26043
26129
|
const mainLandmark = hasMainLandmark();
|
|
@@ -26295,11 +26381,10 @@ if (isAccTreeEligible) {
|
|
|
26295
26381
|
return { ruleId: rule.ruleId, outcome: 'cantTell', severity: 'minor', occurrences };
|
|
26296
26382
|
}), applicability: null },
|
|
26297
26383
|
"combobox-name-present": { run: (function runInPage(ctx) {
|
|
26298
|
-
const { document,
|
|
26384
|
+
const { document, helpers, rule } = ctx;
|
|
26299
26385
|
const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
|
|
26300
26386
|
? helpers.getEligibilityInfo
|
|
26301
26387
|
: null;
|
|
26302
|
-
const safeRoot = root || document;
|
|
26303
26388
|
|
|
26304
26389
|
|
|
26305
26390
|
function normalizeWs(s) {
|
|
@@ -26431,7 +26516,7 @@ if (isAccTreeEligible) {
|
|
|
26431
26516
|
let applicableCount = 0;
|
|
26432
26517
|
|
|
26433
26518
|
const selector = '[role="combobox"]';
|
|
26434
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
26519
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
26435
26520
|
|
|
26436
26521
|
// Precompute label[for] map for combobox elements that are labelable
|
|
26437
26522
|
// native form controls (e.g. <input role="combobox">).
|
|
@@ -28158,14 +28243,13 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
28158
28243
|
return { ruleId: rule.ruleId, outcome: 'fail', severity: rule.defaultSeverity || 'serious', occurrences };
|
|
28159
28244
|
}), applicability: null },
|
|
28160
28245
|
"definition-list-children-valid": { run: (function runInPage(ctx) {
|
|
28161
|
-
const { document,
|
|
28162
|
-
const safeRoot = root || document;
|
|
28246
|
+
const { document, helpers, rule } = ctx;
|
|
28163
28247
|
|
|
28164
28248
|
// Declared inside runInPage — see scripts/build-core.js header
|
|
28165
28249
|
// ("runInPage MUST be self-contained").
|
|
28166
28250
|
const PASSTHROUGH_TAGS = new Set(['dt', 'dd', 'script', 'template', 'style']);
|
|
28167
28251
|
|
|
28168
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('dl'
|
|
28252
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('dl') : helpers.queryAll('dl');
|
|
28169
28253
|
|
|
28170
28254
|
const occurrences = [];
|
|
28171
28255
|
let applicableCount = 0;
|
|
@@ -28249,10 +28333,9 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
28249
28333
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
28250
28334
|
}), applicability: null },
|
|
28251
28335
|
"deprecated-elements-not-used": { run: (function runInPage(ctx) {
|
|
28252
|
-
const { document,
|
|
28253
|
-
const safeRoot = root || document;
|
|
28336
|
+
const { document, helpers, rule } = ctx;
|
|
28254
28337
|
|
|
28255
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('blink, marquee'
|
|
28338
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('blink, marquee') : helpers.queryAll('blink, marquee');
|
|
28256
28339
|
|
|
28257
28340
|
const occurrences = [];
|
|
28258
28341
|
let applicableCount = 0;
|
|
@@ -28291,11 +28374,10 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
28291
28374
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
28292
28375
|
}), applicability: null },
|
|
28293
28376
|
"dialog-name-present": { run: (function runInPage(ctx) {
|
|
28294
|
-
const { document,
|
|
28377
|
+
const { document, helpers, rule } = ctx;
|
|
28295
28378
|
const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
|
|
28296
28379
|
? helpers.getEligibilityInfo
|
|
28297
28380
|
: null;
|
|
28298
|
-
const safeRoot = root || document;
|
|
28299
28381
|
|
|
28300
28382
|
|
|
28301
28383
|
function normalizeWs(s) {
|
|
@@ -28409,7 +28491,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
28409
28491
|
let applicableCount = 0;
|
|
28410
28492
|
|
|
28411
28493
|
const selector = '[role="dialog"],[role="alertdialog"]';
|
|
28412
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
28494
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
28413
28495
|
|
|
28414
28496
|
|
|
28415
28497
|
function evaluate(el) {
|
|
@@ -28474,10 +28556,9 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
28474
28556
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
28475
28557
|
}), applicability: null },
|
|
28476
28558
|
"dlitem-parent-valid": { run: (function runInPage(ctx) {
|
|
28477
|
-
const { document,
|
|
28478
|
-
const safeRoot = root || document;
|
|
28559
|
+
const { document, helpers, rule } = ctx;
|
|
28479
28560
|
|
|
28480
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('dt, dd'
|
|
28561
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('dt, dd') : helpers.queryAll('dt, dd');
|
|
28481
28562
|
|
|
28482
28563
|
const occurrences = [];
|
|
28483
28564
|
let applicableCount = 0;
|
|
@@ -29129,8 +29210,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
29129
29210
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
29130
29211
|
}), applicability: null },
|
|
29131
29212
|
"focus-order-semantics": { run: (function runInPage(ctx) {
|
|
29132
|
-
const { document,
|
|
29133
|
-
const safeRoot = root || document;
|
|
29213
|
+
const { document, helpers, rule } = ctx;
|
|
29134
29214
|
|
|
29135
29215
|
const NON_INTERACTIVE_ROLES = new Set([
|
|
29136
29216
|
'generic', 'group', 'text', 'presentation', 'none', 'img', 'heading',
|
|
@@ -29140,7 +29220,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
29140
29220
|
'definition', 'paragraph', 'caption', 'status', 'alert', 'log', 'tooltip'
|
|
29141
29221
|
]);
|
|
29142
29222
|
|
|
29143
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[tabindex][role]'
|
|
29223
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[tabindex][role]') : helpers.queryAll('[tabindex][role]');
|
|
29144
29224
|
|
|
29145
29225
|
const occurrences = [];
|
|
29146
29226
|
let applicableCount = 0;
|
|
@@ -29781,11 +29861,10 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
29781
29861
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [], data: { details: { metrics } } };
|
|
29782
29862
|
}), applicability: null },
|
|
29783
29863
|
"form-control-single-label": { run: (function runInPage(ctx) {
|
|
29784
|
-
const { document,
|
|
29785
|
-
const safeRoot = root || document;
|
|
29864
|
+
const { document, helpers, rule } = ctx;
|
|
29786
29865
|
|
|
29787
29866
|
const selector = 'input:not([type="hidden"]):not([type="submit"]):not([type="reset"]):not([type="button"]):not([type="image"]),select,textarea';
|
|
29788
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
29867
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
29789
29868
|
|
|
29790
29869
|
// Build a for-value -> label[] map once (avoids per-control dynamic
|
|
29791
29870
|
// attribute-selector construction / CSS.escape, which is not guaranteed
|
|
@@ -30063,14 +30142,13 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
30063
30142
|
return { ruleId: rule.ruleId, outcome: 'fail', severity: rule.defaultSeverity || 'serious', occurrences };
|
|
30064
30143
|
}), applicability: null },
|
|
30065
30144
|
"identical-links-same-purpose": { run: (function runInPage(ctx) {
|
|
30066
|
-
const { document,
|
|
30067
|
-
const safeRoot = root || document;
|
|
30145
|
+
const { document, helpers, rule } = ctx;
|
|
30068
30146
|
|
|
30069
30147
|
function normName(s) {
|
|
30070
30148
|
return (s == null ? '' : String(s)).replace(/\s+/g, ' ').trim().toLowerCase();
|
|
30071
30149
|
}
|
|
30072
30150
|
|
|
30073
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('a[href]'
|
|
30151
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('a[href]') : helpers.queryAll('a[href]');
|
|
30074
30152
|
|
|
30075
30153
|
const groups = new Map(); // normName -> [{ el, href }]
|
|
30076
30154
|
let applicableCount = 0;
|
|
@@ -30150,8 +30228,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
30150
30228
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
30151
30229
|
}), applicability: null },
|
|
30152
30230
|
"iframe-focusable-content": { run: (function runInPage(ctx) {
|
|
30153
|
-
const { document,
|
|
30154
|
-
const safeRoot = root || document;
|
|
30231
|
+
const { document, helpers, rule } = ctx;
|
|
30155
30232
|
|
|
30156
30233
|
function hasFocusableCandidate(doc) {
|
|
30157
30234
|
if (!doc || !doc.querySelectorAll) return false;
|
|
@@ -30183,7 +30260,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
30183
30260
|
return !Number.isNaN(n) && n < 0;
|
|
30184
30261
|
}
|
|
30185
30262
|
|
|
30186
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('iframe, frame'
|
|
30263
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('iframe, frame') : helpers.queryAll('iframe, frame');
|
|
30187
30264
|
|
|
30188
30265
|
const occurrences = [];
|
|
30189
30266
|
let applicableCount = 0;
|
|
@@ -30233,10 +30310,9 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
30233
30310
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
30234
30311
|
}), applicability: null },
|
|
30235
30312
|
"iframe-name-present": { run: (function runInPage(ctx) {
|
|
30236
|
-
const { document,
|
|
30237
|
-
const safeRoot = root || document;
|
|
30313
|
+
const { document, helpers, rule } = ctx;
|
|
30238
30314
|
|
|
30239
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('iframe, frame'
|
|
30315
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('iframe, frame') : helpers.queryAll('iframe, frame');
|
|
30240
30316
|
|
|
30241
30317
|
const occurrences = [];
|
|
30242
30318
|
let applicableCount = 0;
|
|
@@ -30288,10 +30364,9 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
30288
30364
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
30289
30365
|
}), applicability: null },
|
|
30290
30366
|
"iframe-title-unique": { run: (function runInPage(ctx) {
|
|
30291
|
-
const { document,
|
|
30292
|
-
const safeRoot = root || document;
|
|
30367
|
+
const { document, helpers, rule } = ctx;
|
|
30293
30368
|
|
|
30294
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('iframe, frame'
|
|
30369
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('iframe, frame') : helpers.queryAll('iframe, frame');
|
|
30295
30370
|
|
|
30296
30371
|
const groups = new Map(); // trimmed title -> elements[]
|
|
30297
30372
|
let applicableCount = 0;
|
|
@@ -30346,8 +30421,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
30346
30421
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
30347
30422
|
}), applicability: null },
|
|
30348
30423
|
"image-redundant-alt": { run: (function runInPage(ctx) {
|
|
30349
|
-
const { document,
|
|
30350
|
-
const safeRoot = root || document;
|
|
30424
|
+
const { document, helpers, rule } = ctx;
|
|
30351
30425
|
|
|
30352
30426
|
function normalizeWs(s) {
|
|
30353
30427
|
return String(s || '').replace(/\s+/g, ' ').trim();
|
|
@@ -30366,7 +30440,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
30366
30440
|
return normalizeWs(text);
|
|
30367
30441
|
}
|
|
30368
30442
|
|
|
30369
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('img[alt]'
|
|
30443
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('img[alt]') : helpers.queryAll('img[alt]');
|
|
30370
30444
|
|
|
30371
30445
|
const occurrences = [];
|
|
30372
30446
|
let applicableCount = 0;
|
|
@@ -31154,8 +31228,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
31154
31228
|
return { ruleId: rule.ruleId, outcome: 'cantTell', severity: 'minor', occurrences };
|
|
31155
31229
|
}), applicability: null },
|
|
31156
31230
|
"label-in-name": { run: (function runInPage(ctx) {
|
|
31157
|
-
const { document,
|
|
31158
|
-
const safeRoot = root || document;
|
|
31231
|
+
const { document, helpers, rule } = ctx;
|
|
31159
31232
|
|
|
31160
31233
|
const occurrences = [];
|
|
31161
31234
|
let applicableCount = 0;
|
|
@@ -31164,7 +31237,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
31164
31237
|
// NOTE: aria-hidden is intentionally NOT excluded here; it does not affect visual rendering.
|
|
31165
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])';
|
|
31166
31239
|
|
|
31167
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
31240
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
31168
31241
|
|
|
31169
31242
|
function norm(s) {
|
|
31170
31243
|
const v = (s == null ? '' : String(s));
|
|
@@ -31302,11 +31375,11 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
31302
31375
|
}
|
|
31303
31376
|
|
|
31304
31377
|
// Fallback: label[for=id]. Uses `document` directly rather than
|
|
31305
|
-
// ctx.root
|
|
31306
|
-
//
|
|
31307
|
-
//
|
|
31308
|
-
//
|
|
31309
|
-
//
|
|
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.
|
|
31310
31383
|
try {
|
|
31311
31384
|
const idAttribute = control && control.getAttribute ? (control.getAttribute('id') || '') : '';
|
|
31312
31385
|
const key = String(idAttribute || '').trim();
|
|
@@ -31447,11 +31520,10 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
31447
31520
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
31448
31521
|
}), applicability: null },
|
|
31449
31522
|
"label-title-only": { run: (function runInPage(ctx) {
|
|
31450
|
-
const { document,
|
|
31451
|
-
const safeRoot = root || document;
|
|
31523
|
+
const { document, helpers, rule } = ctx;
|
|
31452
31524
|
|
|
31453
31525
|
const selector = 'input:not([type="hidden"]):not([type="submit"]):not([type="reset"]):not([type="button"]):not([type="image"]),select,textarea';
|
|
31454
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
31526
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
31455
31527
|
|
|
31456
31528
|
const labelsByFor = new Map();
|
|
31457
31529
|
const allLabels = document.getElementsByTagName ? document.getElementsByTagName('label') : [];
|
|
@@ -32498,8 +32570,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
32498
32570
|
return { ruleId: rule.ruleId, outcome: 'cantTell', severity: rule.defaultSeverity || 'minor', occurrences };
|
|
32499
32571
|
}), applicability: null },
|
|
32500
32572
|
"link-in-text-block": { run: (function runInPage(ctx) {
|
|
32501
|
-
const { document,
|
|
32502
|
-
const safeRoot = root || document;
|
|
32573
|
+
const { document, helpers, rule, engineOptions } = ctx;
|
|
32503
32574
|
|
|
32504
32575
|
function safeComputedStyle(el) {
|
|
32505
32576
|
try {
|
|
@@ -32545,7 +32616,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
32545
32616
|
const c = helpers && helpers.contrast ? helpers.contrast : null;
|
|
32546
32617
|
|
|
32547
32618
|
const selector = 'a[href]';
|
|
32548
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
32619
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
32549
32620
|
|
|
32550
32621
|
const occurrences = [];
|
|
32551
32622
|
let applicableCount = 0;
|
|
@@ -32645,8 +32716,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
32645
32716
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
32646
32717
|
}), applicability: null },
|
|
32647
32718
|
"link-name-present": { run: (function runInPage(ctx) {
|
|
32648
|
-
const { document,
|
|
32649
|
-
const safeRoot = root || document;
|
|
32719
|
+
const { document, helpers, rule } = ctx;
|
|
32650
32720
|
|
|
32651
32721
|
const occurrences = [];
|
|
32652
32722
|
let applicableCount = 0;
|
|
@@ -32667,7 +32737,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
32667
32737
|
}
|
|
32668
32738
|
|
|
32669
32739
|
const selector = 'a[href], area[href], [role="link"]';
|
|
32670
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
32740
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
32671
32741
|
|
|
32672
32742
|
for (const el of nodes) {
|
|
32673
32743
|
// isAccTreeEligible returns { eligible, reasons }, not a boolean.
|
|
@@ -32680,7 +32750,20 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
32680
32750
|
const nameInfo = helpers.getAccessibleNameInfo ? helpers.getAccessibleNameInfo(el, ctx) : null;
|
|
32681
32751
|
const programmaticName = (nameInfo && typeof nameInfo.value === 'string') ? nameInfo.value : '';
|
|
32682
32752
|
|
|
32683
|
-
|
|
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) : '';
|
|
32684
32767
|
const finalName = (programmaticName.trim().length ? programmaticName : contentName).trim();
|
|
32685
32768
|
|
|
32686
32769
|
if (finalName.length === 0) {
|
|
@@ -32732,8 +32815,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
32732
32815
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
32733
32816
|
}), applicability: null },
|
|
32734
32817
|
"link-name-quality": { run: (function runInPage(ctx) {
|
|
32735
|
-
const { document,
|
|
32736
|
-
const safeRoot = root || document;
|
|
32818
|
+
const { document, helpers, rule } = ctx;
|
|
32737
32819
|
|
|
32738
32820
|
const GENERIC_LINK_TEXT = new Set([
|
|
32739
32821
|
'click here', 'here', 'click', 'more', 'more info', 'more information',
|
|
@@ -32752,7 +32834,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
32752
32834
|
}
|
|
32753
32835
|
|
|
32754
32836
|
const selector = 'a[href]';
|
|
32755
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
32837
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
32756
32838
|
|
|
32757
32839
|
const occurrences = [];
|
|
32758
32840
|
let applicableCount = 0;
|
|
@@ -32812,14 +32894,13 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
32812
32894
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
32813
32895
|
}), applicability: null },
|
|
32814
32896
|
"list-children-valid": { run: (function runInPage(ctx) {
|
|
32815
|
-
const { document,
|
|
32816
|
-
const safeRoot = root || document;
|
|
32897
|
+
const { document, helpers, rule } = ctx;
|
|
32817
32898
|
|
|
32818
32899
|
// Declared inside runInPage — see scripts/build-core.js header
|
|
32819
32900
|
// ("runInPage MUST be self-contained").
|
|
32820
32901
|
const ALLOWED_CHILD_TAGS = new Set(['li', 'script', 'template']);
|
|
32821
32902
|
|
|
32822
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('ul, ol'
|
|
32903
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('ul, ol') : helpers.queryAll('ul, ol');
|
|
32823
32904
|
|
|
32824
32905
|
const isAccTreeEligible = helpers && typeof helpers.isAccTreeEligible === 'function' ? helpers.isAccTreeEligible : null;
|
|
32825
32906
|
|
|
@@ -32890,11 +32971,10 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
32890
32971
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
32891
32972
|
}), applicability: null },
|
|
32892
32973
|
"listbox-name-present": { run: (function runInPage(ctx) {
|
|
32893
|
-
const { document,
|
|
32974
|
+
const { document, helpers, rule } = ctx;
|
|
32894
32975
|
const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
|
|
32895
32976
|
? helpers.getEligibilityInfo
|
|
32896
32977
|
: null;
|
|
32897
|
-
const safeRoot = root || document;
|
|
32898
32978
|
|
|
32899
32979
|
|
|
32900
32980
|
function normalizeWs(s) {
|
|
@@ -32999,7 +33079,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
32999
33079
|
let applicableCount = 0;
|
|
33000
33080
|
|
|
33001
33081
|
const selector = "[role=\"listbox\"]";
|
|
33002
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
33082
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
33003
33083
|
|
|
33004
33084
|
// Precompute label[for] map for listbox elements that are labelable
|
|
33005
33085
|
// native form controls (e.g. <select multiple role="listbox">).
|
|
@@ -33100,10 +33180,9 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
33100
33180
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
33101
33181
|
}), applicability: null },
|
|
33102
33182
|
"listitem-parent-valid": { run: (function runInPage(ctx) {
|
|
33103
|
-
const { document,
|
|
33104
|
-
const safeRoot = root || document;
|
|
33183
|
+
const { document, helpers, rule } = ctx;
|
|
33105
33184
|
|
|
33106
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('li'
|
|
33185
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('li') : helpers.queryAll('li');
|
|
33107
33186
|
|
|
33108
33187
|
const occurrences = [];
|
|
33109
33188
|
let applicableCount = 0;
|
|
@@ -33532,11 +33611,10 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
33532
33611
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
33533
33612
|
}), applicability: null },
|
|
33534
33613
|
"menuitem-name-present": { run: (function runInPage(ctx) {
|
|
33535
|
-
const { document,
|
|
33614
|
+
const { document, helpers, rule } = ctx;
|
|
33536
33615
|
const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
|
|
33537
33616
|
? helpers.getEligibilityInfo
|
|
33538
33617
|
: null;
|
|
33539
|
-
const safeRoot = root || document;
|
|
33540
33618
|
|
|
33541
33619
|
|
|
33542
33620
|
function normalizeWs(s) {
|
|
@@ -33642,7 +33720,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
33642
33720
|
let applicableCount = 0;
|
|
33643
33721
|
|
|
33644
33722
|
const selector = '[role="menuitem"],[role="menuitemcheckbox"],[role="menuitemradio"]';
|
|
33645
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
33723
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
33646
33724
|
|
|
33647
33725
|
|
|
33648
33726
|
function evaluate(el) {
|
|
@@ -33948,11 +34026,10 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
33948
34026
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
33949
34027
|
}), applicability: null },
|
|
33950
34028
|
"meter-name-present": { run: (function runInPage(ctx) {
|
|
33951
|
-
const { document,
|
|
34029
|
+
const { document, helpers, rule } = ctx;
|
|
33952
34030
|
const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
|
|
33953
34031
|
? helpers.getEligibilityInfo
|
|
33954
34032
|
: null;
|
|
33955
|
-
const safeRoot = root || document;
|
|
33956
34033
|
|
|
33957
34034
|
|
|
33958
34035
|
function normalizeWs(s) {
|
|
@@ -34016,7 +34093,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
34016
34093
|
let applicableCount = 0;
|
|
34017
34094
|
|
|
34018
34095
|
const selector = '[role="meter"]';
|
|
34019
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
34096
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
34020
34097
|
|
|
34021
34098
|
|
|
34022
34099
|
function evaluate(el) {
|
|
@@ -34082,8 +34159,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
34082
34159
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
34083
34160
|
}), applicability: null },
|
|
34084
34161
|
"mouse-only-event-handlers": { run: (function runInPage(ctx) {
|
|
34085
|
-
const { document,
|
|
34086
|
-
const safeRoot = root || document;
|
|
34162
|
+
const { document, helpers, rule } = ctx;
|
|
34087
34163
|
|
|
34088
34164
|
const MOUSE_ONLY_ATTRS = [
|
|
34089
34165
|
'onmouseover', 'onmouseout', 'onmousedown', 'onmouseup',
|
|
@@ -34094,7 +34170,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
34094
34170
|
function trim(v) { return (v == null ? '' : String(v)).trim(); }
|
|
34095
34171
|
|
|
34096
34172
|
const selector = MOUSE_ONLY_ATTRS.map((a) => `[${a}]`).join(', ');
|
|
34097
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
34173
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
34098
34174
|
|
|
34099
34175
|
const occurrences = [];
|
|
34100
34176
|
let applicableCount = 0;
|
|
@@ -34146,8 +34222,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
34146
34222
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
34147
34223
|
}), applicability: null },
|
|
34148
34224
|
"nested-interactive-controls-absent": { run: (function runInPage(ctx) {
|
|
34149
|
-
const { document,
|
|
34150
|
-
const safeRoot = root || document;
|
|
34225
|
+
const { document, helpers, rule } = ctx;
|
|
34151
34226
|
|
|
34152
34227
|
// Declared inside runInPage — see scripts/build-core.js header
|
|
34153
34228
|
// ("runInPage MUST be self-contained").
|
|
@@ -34176,7 +34251,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
34176
34251
|
'[role="treeitem"]'
|
|
34177
34252
|
].join(', ');
|
|
34178
34253
|
|
|
34179
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(INTERACTIVE_SELECTOR
|
|
34254
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(INTERACTIVE_SELECTOR) : helpers.queryAll(INTERACTIVE_SELECTOR);
|
|
34180
34255
|
|
|
34181
34256
|
const occurrences = [];
|
|
34182
34257
|
let applicableCount = 0;
|
|
@@ -34225,10 +34300,9 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
34225
34300
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
34226
34301
|
}), applicability: null },
|
|
34227
34302
|
"no-autoplay-audio": { run: (function runInPage(ctx) {
|
|
34228
|
-
const { document,
|
|
34229
|
-
const safeRoot = root || document;
|
|
34303
|
+
const { document, helpers, rule } = ctx;
|
|
34230
34304
|
|
|
34231
|
-
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]');
|
|
34232
34306
|
|
|
34233
34307
|
const occurrences = [];
|
|
34234
34308
|
let applicableCount = 0;
|
|
@@ -34582,11 +34656,10 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
34582
34656
|
return {ruleId: rule.ruleId, outcome: 'cantTell', severity: 'minor', occurrences};
|
|
34583
34657
|
}), applicability: null },
|
|
34584
34658
|
"option-name-present": { run: (function runInPage(ctx) {
|
|
34585
|
-
const { document,
|
|
34659
|
+
const { document, helpers, rule } = ctx;
|
|
34586
34660
|
const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
|
|
34587
34661
|
? helpers.getEligibilityInfo
|
|
34588
34662
|
: null;
|
|
34589
|
-
const safeRoot = root || document;
|
|
34590
34663
|
|
|
34591
34664
|
|
|
34592
34665
|
function normalizeWs(s) {
|
|
@@ -34650,7 +34723,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
34650
34723
|
let applicableCount = 0;
|
|
34651
34724
|
|
|
34652
34725
|
const selector = "[role=\"option\"]";
|
|
34653
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
34726
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
34654
34727
|
|
|
34655
34728
|
|
|
34656
34729
|
function hasName(el) {
|
|
@@ -34713,8 +34786,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
34713
34786
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
34714
34787
|
}), applicability: null },
|
|
34715
34788
|
"p-as-heading": { run: (function runInPage(ctx) {
|
|
34716
|
-
const { document,
|
|
34717
|
-
const safeRoot = root || document;
|
|
34789
|
+
const { document, helpers, rule } = ctx;
|
|
34718
34790
|
|
|
34719
34791
|
const MAX_HEADING_LIKE_CHARS = 120;
|
|
34720
34792
|
const MIN_FONT_SIZE_PX = 18;
|
|
@@ -34766,7 +34838,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
34766
34838
|
return Number.isFinite(px) ? px : 0;
|
|
34767
34839
|
}
|
|
34768
34840
|
|
|
34769
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('p'
|
|
34841
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('p') : helpers.queryAll('p');
|
|
34770
34842
|
|
|
34771
34843
|
const occurrences = [];
|
|
34772
34844
|
let applicableCount = 0;
|
|
@@ -34848,14 +34920,31 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
34848
34920
|
return tag === 'h1';
|
|
34849
34921
|
}
|
|
34850
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.
|
|
34851
34938
|
let nodes = [];
|
|
34852
34939
|
try {
|
|
34853
|
-
nodes =
|
|
34940
|
+
nodes = helpers && typeof helpers.queryAllSmart === 'function'
|
|
34941
|
+
? helpers.queryAllSmart('h1, [role]')
|
|
34942
|
+
: document.querySelectorAll('h1, [role]');
|
|
34854
34943
|
} catch {
|
|
34855
34944
|
nodes = [];
|
|
34856
34945
|
}
|
|
34857
34946
|
|
|
34858
|
-
const hasH1 = Array.from(nodes).some((el) => el && isLevelOneHeading(el));
|
|
34947
|
+
const hasH1 = Array.from(nodes).some((el) => el && isLevelOneHeading(el) && isExposedToAt(el));
|
|
34859
34948
|
|
|
34860
34949
|
if (hasH1) {
|
|
34861
34950
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
@@ -35179,8 +35268,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
35179
35268
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
35180
35269
|
}), applicability: null },
|
|
35181
35270
|
"presentation-role-conflict": { run: (function runInPage(ctx) {
|
|
35182
|
-
const { document,
|
|
35183
|
-
const safeRoot = root || document;
|
|
35271
|
+
const { document, helpers, rule } = ctx;
|
|
35184
35272
|
|
|
35185
35273
|
// The full set of ARIA attributes marked `global: true` per the WAI-ARIA
|
|
35186
35274
|
// spec (confirmed against a widely-used reference engine's own
|
|
@@ -35197,8 +35285,8 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
35197
35285
|
const getFocusableInfo = helpers && typeof helpers.getFocusableInfo === 'function' ? helpers.getFocusableInfo : null;
|
|
35198
35286
|
|
|
35199
35287
|
const nodes = helpers.queryAllSmart
|
|
35200
|
-
? helpers.queryAllSmart('[role="presentation"], [role="none"], img[alt=""]'
|
|
35201
|
-
: 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=""]');
|
|
35202
35290
|
|
|
35203
35291
|
const occurrences = [];
|
|
35204
35292
|
let applicableCount = 0;
|
|
@@ -35262,11 +35350,10 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
35262
35350
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
35263
35351
|
}), applicability: null },
|
|
35264
35352
|
"progressbar-name-present": { run: (function runInPage(ctx) {
|
|
35265
|
-
const { document,
|
|
35353
|
+
const { document, helpers, rule } = ctx;
|
|
35266
35354
|
const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
|
|
35267
35355
|
? helpers.getEligibilityInfo
|
|
35268
35356
|
: null;
|
|
35269
|
-
const safeRoot = root || document;
|
|
35270
35357
|
|
|
35271
35358
|
|
|
35272
35359
|
function normalizeWs(s) {
|
|
@@ -35330,7 +35417,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
35330
35417
|
let applicableCount = 0;
|
|
35331
35418
|
|
|
35332
35419
|
const selector = '[role="progressbar"]';
|
|
35333
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
35420
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
35334
35421
|
|
|
35335
35422
|
|
|
35336
35423
|
function evaluate(el) {
|
|
@@ -35742,8 +35829,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
35742
35829
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
35743
35830
|
}), applicability: null },
|
|
35744
35831
|
"scrollable-region-focusable": { run: (function runInPage(ctx) {
|
|
35745
|
-
const { document,
|
|
35746
|
-
const safeRoot = root || document;
|
|
35832
|
+
const { document, helpers, rule } = ctx;
|
|
35747
35833
|
|
|
35748
35834
|
function safeComputedStyle(el) {
|
|
35749
35835
|
try {
|
|
@@ -35788,7 +35874,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
35788
35874
|
}
|
|
35789
35875
|
|
|
35790
35876
|
const CANDIDATE_SELECTOR = 'div, section, article, aside, main, nav, pre, table, blockquote, ul, ol, textarea';
|
|
35791
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(CANDIDATE_SELECTOR
|
|
35877
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(CANDIDATE_SELECTOR) : helpers.queryAll(CANDIDATE_SELECTOR);
|
|
35792
35878
|
|
|
35793
35879
|
const occurrences = [];
|
|
35794
35880
|
let applicableCount = 0;
|
|
@@ -35843,11 +35929,10 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
35843
35929
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
35844
35930
|
}), applicability: null },
|
|
35845
35931
|
"searchbox-name-present": { run: (function runInPage(ctx) {
|
|
35846
|
-
const { document,
|
|
35932
|
+
const { document, helpers, rule } = ctx;
|
|
35847
35933
|
const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
|
|
35848
35934
|
? helpers.getEligibilityInfo
|
|
35849
35935
|
: null;
|
|
35850
|
-
const safeRoot = root || document;
|
|
35851
35936
|
|
|
35852
35937
|
|
|
35853
35938
|
function normalizeWs(s) {
|
|
@@ -35952,7 +36037,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
35952
36037
|
let applicableCount = 0;
|
|
35953
36038
|
|
|
35954
36039
|
const selector = "[role=\"searchbox\"]";
|
|
35955
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
36040
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
35956
36041
|
|
|
35957
36042
|
// Precompute label[for] map for searchbox elements that are labelable
|
|
35958
36043
|
// native form controls (e.g. <input role="searchbox">).
|
|
@@ -36053,10 +36138,9 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
36053
36138
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
36054
36139
|
}), applicability: null },
|
|
36055
36140
|
"server-side-image-map-absent": { run: (function runInPage(ctx) {
|
|
36056
|
-
const { document,
|
|
36057
|
-
const safeRoot = root || document;
|
|
36141
|
+
const { document, helpers, rule } = ctx;
|
|
36058
36142
|
|
|
36059
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('img[ismap]'
|
|
36143
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('img[ismap]') : helpers.queryAll('img[ismap]');
|
|
36060
36144
|
|
|
36061
36145
|
const occurrences = [];
|
|
36062
36146
|
let applicableCount = 0;
|
|
@@ -36094,8 +36178,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
36094
36178
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
36095
36179
|
}), applicability: null },
|
|
36096
36180
|
"skip-link": { run: (function runInPage(ctx) {
|
|
36097
|
-
const { document,
|
|
36098
|
-
const safeRoot = root || document;
|
|
36181
|
+
const { document, helpers, rule } = ctx;
|
|
36099
36182
|
|
|
36100
36183
|
function normalizeWs(s) {
|
|
36101
36184
|
return String(s || '').replace(/\s+/g, ' ').trim();
|
|
@@ -36122,7 +36205,31 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
36122
36205
|
return normalizeWs(el.textContent);
|
|
36123
36206
|
}
|
|
36124
36207
|
|
|
36125
|
-
|
|
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]');
|
|
36126
36233
|
|
|
36127
36234
|
const occurrences = [];
|
|
36128
36235
|
let applicableCount = 0;
|
|
@@ -36160,7 +36267,63 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
36160
36267
|
}
|
|
36161
36268
|
}
|
|
36162
36269
|
|
|
36163
|
-
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
|
+
}
|
|
36164
36327
|
|
|
36165
36328
|
const stableSelector = helpers.buildSelector ? helpers.buildSelector(el) : 'html';
|
|
36166
36329
|
const html = helpers.getOuterHtmlSnippet ? helpers.getOuterHtmlSnippet(el) : (el.outerHTML || '');
|
|
@@ -36190,11 +36353,10 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
36190
36353
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
36191
36354
|
}), applicability: null },
|
|
36192
36355
|
"slider-name-present": { run: (function runInPage(ctx) {
|
|
36193
|
-
const { document,
|
|
36356
|
+
const { document, helpers, rule } = ctx;
|
|
36194
36357
|
const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
|
|
36195
36358
|
? helpers.getEligibilityInfo
|
|
36196
36359
|
: null;
|
|
36197
|
-
const safeRoot = root || document;
|
|
36198
36360
|
|
|
36199
36361
|
|
|
36200
36362
|
function normalizeWs(s) {
|
|
@@ -36326,7 +36488,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
36326
36488
|
let applicableCount = 0;
|
|
36327
36489
|
|
|
36328
36490
|
const selector = 'input[type="range"], [role="slider"]';
|
|
36329
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
36491
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
36330
36492
|
|
|
36331
36493
|
|
|
36332
36494
|
// Precompute label[for] map for native range inputs.
|
|
@@ -36440,11 +36602,10 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
36440
36602
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
36441
36603
|
}), applicability: null },
|
|
36442
36604
|
"spinbutton-name-present": { run: (function runInPage(ctx) {
|
|
36443
|
-
const { document,
|
|
36605
|
+
const { document, helpers, rule } = ctx;
|
|
36444
36606
|
const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
|
|
36445
36607
|
? helpers.getEligibilityInfo
|
|
36446
36608
|
: null;
|
|
36447
|
-
const safeRoot = root || document;
|
|
36448
36609
|
|
|
36449
36610
|
|
|
36450
36611
|
function normalizeWs(s) {
|
|
@@ -36549,7 +36710,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
36549
36710
|
let applicableCount = 0;
|
|
36550
36711
|
|
|
36551
36712
|
const selector = "[role=\"spinbutton\"]";
|
|
36552
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
36713
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
36553
36714
|
|
|
36554
36715
|
// Precompute label[for] map for spinbutton elements that are labelable
|
|
36555
36716
|
// native form controls (e.g. <input role="spinbutton">).
|
|
@@ -36650,11 +36811,10 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
36650
36811
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
36651
36812
|
}), applicability: null },
|
|
36652
36813
|
"summary-name-present": { run: (function runInPage(ctx) {
|
|
36653
|
-
const { document,
|
|
36814
|
+
const { document, helpers, rule } = ctx;
|
|
36654
36815
|
const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
|
|
36655
36816
|
? helpers.getEligibilityInfo
|
|
36656
36817
|
: null;
|
|
36657
|
-
const safeRoot = root || document;
|
|
36658
36818
|
|
|
36659
36819
|
|
|
36660
36820
|
function normalizeWs(s) {
|
|
@@ -36717,7 +36877,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
36717
36877
|
const occurrences = [];
|
|
36718
36878
|
let applicableCount = 0;
|
|
36719
36879
|
|
|
36720
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('summary'
|
|
36880
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('summary') : helpers.queryAll('summary');
|
|
36721
36881
|
|
|
36722
36882
|
function evaluate(el) {
|
|
36723
36883
|
const ariaLabel = getAttr(el, 'aria-label');
|
|
@@ -37356,11 +37516,10 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
37356
37516
|
return {ruleId: rule.ruleId, outcome: 'cantTell', severity: 'minor', occurrences};
|
|
37357
37517
|
}), applicability: null },
|
|
37358
37518
|
"tab-name-present": { run: (function runInPage(ctx) {
|
|
37359
|
-
const { document,
|
|
37519
|
+
const { document, helpers, rule } = ctx;
|
|
37360
37520
|
const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
|
|
37361
37521
|
? helpers.getEligibilityInfo
|
|
37362
37522
|
: null;
|
|
37363
|
-
const safeRoot = root || document;
|
|
37364
37523
|
|
|
37365
37524
|
|
|
37366
37525
|
function normalizeWs(s) {
|
|
@@ -37466,7 +37625,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
37466
37625
|
let applicableCount = 0;
|
|
37467
37626
|
|
|
37468
37627
|
const selector = '[role="tab"]';
|
|
37469
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
37628
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
37470
37629
|
|
|
37471
37630
|
|
|
37472
37631
|
function evaluate(el) {
|
|
@@ -37581,14 +37740,13 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
37581
37740
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
37582
37741
|
}), applicability: null },
|
|
37583
37742
|
"table-duplicate-name": { run: (function runInPage(ctx) {
|
|
37584
|
-
const { document,
|
|
37585
|
-
const safeRoot = root || document;
|
|
37743
|
+
const { document, helpers, rule } = ctx;
|
|
37586
37744
|
|
|
37587
37745
|
function normalizeWs(s) {
|
|
37588
37746
|
return String(s || '').replace(/\s+/g, ' ').trim();
|
|
37589
37747
|
}
|
|
37590
37748
|
|
|
37591
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('table[summary]'
|
|
37749
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('table[summary]') : helpers.queryAll('table[summary]');
|
|
37592
37750
|
|
|
37593
37751
|
const occurrences = [];
|
|
37594
37752
|
let applicableCount = 0;
|
|
@@ -37634,12 +37792,11 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
37634
37792
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
37635
37793
|
}), applicability: null },
|
|
37636
37794
|
"table-fake-caption": { run: (function runInPage(ctx) {
|
|
37637
|
-
const { document,
|
|
37638
|
-
const safeRoot = root || document;
|
|
37795
|
+
const { document, helpers, rule } = ctx;
|
|
37639
37796
|
|
|
37640
37797
|
function trim(v) { return (v == null ? '' : String(v)).trim(); }
|
|
37641
37798
|
|
|
37642
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('table'
|
|
37799
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('table') : helpers.queryAll('table');
|
|
37643
37800
|
|
|
37644
37801
|
const occurrences = [];
|
|
37645
37802
|
let applicableCount = 0;
|
|
@@ -37702,10 +37859,9 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
37702
37859
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
37703
37860
|
}), applicability: null },
|
|
37704
37861
|
"table-headers-attr-valid": { run: (function runInPage(ctx) {
|
|
37705
|
-
const { document,
|
|
37706
|
-
const safeRoot = root || document;
|
|
37862
|
+
const { document, helpers, rule } = ctx;
|
|
37707
37863
|
|
|
37708
|
-
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]');
|
|
37709
37865
|
|
|
37710
37866
|
const occurrences = [];
|
|
37711
37867
|
let applicableCount = 0;
|
|
@@ -37779,10 +37935,9 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
37779
37935
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
37780
37936
|
}), applicability: null },
|
|
37781
37937
|
"table-th-has-data-cells": { run: (function runInPage(ctx) {
|
|
37782
|
-
const { document,
|
|
37783
|
-
const safeRoot = root || document;
|
|
37938
|
+
const { document, helpers, rule } = ctx;
|
|
37784
37939
|
|
|
37785
|
-
const tables = helpers.queryAllSmart ? helpers.queryAllSmart('table'
|
|
37940
|
+
const tables = helpers.queryAllSmart ? helpers.queryAllSmart('table') : helpers.queryAll('table');
|
|
37786
37941
|
|
|
37787
37942
|
const occurrences = [];
|
|
37788
37943
|
let applicableCount = 0;
|
|
@@ -38341,14 +38496,13 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
38341
38496
|
return { ruleId: RULE_ID, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
38342
38497
|
}), applicability: null },
|
|
38343
38498
|
"td-has-header": { run: (function runInPage(ctx) {
|
|
38344
|
-
const { document,
|
|
38345
|
-
const safeRoot = root || document;
|
|
38499
|
+
const { document, helpers, rule } = ctx;
|
|
38346
38500
|
|
|
38347
38501
|
const MIN_SIZE = 4;
|
|
38348
38502
|
|
|
38349
38503
|
function trim(v) { return (v == null ? '' : String(v)).trim(); }
|
|
38350
38504
|
|
|
38351
|
-
const tables = helpers.queryAllSmart ? helpers.queryAllSmart('table'
|
|
38505
|
+
const tables = helpers.queryAllSmart ? helpers.queryAllSmart('table') : helpers.queryAll('table');
|
|
38352
38506
|
|
|
38353
38507
|
const occurrences = [];
|
|
38354
38508
|
let applicableCount = 0;
|
|
@@ -38434,11 +38588,10 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
38434
38588
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
38435
38589
|
}), applicability: null },
|
|
38436
38590
|
"textbox-name-present": { run: (function runInPage(ctx) {
|
|
38437
|
-
const { document,
|
|
38591
|
+
const { document, helpers, rule } = ctx;
|
|
38438
38592
|
const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
|
|
38439
38593
|
? helpers.getEligibilityInfo
|
|
38440
38594
|
: null;
|
|
38441
|
-
const safeRoot = root || document;
|
|
38442
38595
|
|
|
38443
38596
|
|
|
38444
38597
|
function normalizeWs(s) {
|
|
@@ -38543,7 +38696,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
38543
38696
|
let applicableCount = 0;
|
|
38544
38697
|
|
|
38545
38698
|
const selector = "[role=\"textbox\"]";
|
|
38546
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
38699
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
38547
38700
|
|
|
38548
38701
|
// Precompute label[for] map for textbox elements that are labelable
|
|
38549
38702
|
// native form controls (e.g. <input role="textbox">).
|
|
@@ -38644,11 +38797,10 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
38644
38797
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
38645
38798
|
}), applicability: null },
|
|
38646
38799
|
"tooltip-name-present": { run: (function runInPage(ctx) {
|
|
38647
|
-
const { document,
|
|
38800
|
+
const { document, helpers, rule } = ctx;
|
|
38648
38801
|
const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
|
|
38649
38802
|
? helpers.getEligibilityInfo
|
|
38650
38803
|
: null;
|
|
38651
|
-
const safeRoot = root || document;
|
|
38652
38804
|
|
|
38653
38805
|
|
|
38654
38806
|
function normalizeWs(s) {
|
|
@@ -38712,7 +38864,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
38712
38864
|
let applicableCount = 0;
|
|
38713
38865
|
|
|
38714
38866
|
const selector = '[role="tooltip"]';
|
|
38715
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
38867
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
38716
38868
|
|
|
38717
38869
|
|
|
38718
38870
|
function evaluate(el) {
|
|
@@ -38776,11 +38928,10 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
38776
38928
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
38777
38929
|
}), applicability: null },
|
|
38778
38930
|
"treeitem-name-present": { run: (function runInPage(ctx) {
|
|
38779
|
-
const { document,
|
|
38931
|
+
const { document, helpers, rule } = ctx;
|
|
38780
38932
|
const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
|
|
38781
38933
|
? helpers.getEligibilityInfo
|
|
38782
38934
|
: null;
|
|
38783
|
-
const safeRoot = root || document;
|
|
38784
38935
|
|
|
38785
38936
|
|
|
38786
38937
|
function normalizeWs(s) {
|
|
@@ -38844,7 +38995,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
38844
38995
|
let applicableCount = 0;
|
|
38845
38996
|
|
|
38846
38997
|
const selector = "[role=\"treeitem\"]";
|
|
38847
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
38998
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
38848
38999
|
|
|
38849
39000
|
|
|
38850
39001
|
function hasName(el) {
|
|
@@ -38907,12 +39058,11 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
38907
39058
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
38908
39059
|
}), applicability: null },
|
|
38909
39060
|
"valid-lang": { run: (function runInPage(ctx) {
|
|
38910
|
-
const { document,
|
|
38911
|
-
const safeRoot = root || document;
|
|
39061
|
+
const { document, helpers, rule } = ctx;
|
|
38912
39062
|
|
|
38913
39063
|
const BCP47_RE = /^[a-zA-Z]{2,3}(-[a-zA-Z0-9]{2,8})*$/;
|
|
38914
39064
|
|
|
38915
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[lang]'
|
|
39065
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[lang]') : helpers.queryAll('[lang]');
|
|
38916
39066
|
|
|
38917
39067
|
const occurrences = [];
|
|
38918
39068
|
let applicableCount = 0;
|
|
@@ -38957,10 +39107,9 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
38957
39107
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
38958
39108
|
}), applicability: null },
|
|
38959
39109
|
"video-caption": { run: (function runInPage(ctx) {
|
|
38960
|
-
const { document,
|
|
38961
|
-
const safeRoot = root || document;
|
|
39110
|
+
const { document, helpers, rule } = ctx;
|
|
38962
39111
|
|
|
38963
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('video'
|
|
39112
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('video') : helpers.queryAll('video');
|
|
38964
39113
|
|
|
38965
39114
|
const occurrences = [];
|
|
38966
39115
|
let applicableCount = 0;
|
|
@@ -39682,10 +39831,12 @@ const I18N = {
|
|
|
39682
39831
|
"region_description": "Checks that direct children of <body> with visible text content are contained within a landmark region.",
|
|
39683
39832
|
"region_summary_cantTell": "This content is not contained within a landmark region.",
|
|
39684
39833
|
"region_hint_cantTell": "Move this content inside a landmark region (main, nav, aside, a labeled section, etc.).",
|
|
39685
|
-
"skipLink_title": "Skip link must have a resolvable target",
|
|
39686
|
-
"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.",
|
|
39687
39836
|
"skipLink_summary_cantTell": "This skip link's target does not exist.",
|
|
39688
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.",
|
|
39689
39840
|
"autocompleteValid_title": "autocomplete attribute must be a valid autofill value",
|
|
39690
39841
|
"autocompleteValid_description": "Checks that a non-empty autocomplete attribute is \"on\"/\"off\" or a well-formed autofill detail token list.",
|
|
39691
39842
|
"autocompleteValid_summary_fail": "This autocomplete attribute value is not a valid autofill value.",
|
|
@@ -40284,10 +40435,12 @@ const I18N = {
|
|
|
40284
40435
|
"region_description": "Vérifie que les enfants directs de <body> ayant un contenu textuel visible sont contenus dans un point de repère.",
|
|
40285
40436
|
"region_summary_cantTell": "Ce contenu n’est contenu dans aucun point de repère.",
|
|
40286
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.).",
|
|
40287
|
-
"skipLink_title": "Un lien d’évitement doit avoir une cible qui se résout",
|
|
40288
|
-
"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.",
|
|
40289
40440
|
"skipLink_summary_cantTell": "La cible de ce lien d’évitement n’existe pas.",
|
|
40290
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.",
|
|
40291
40444
|
"autocompleteValid_title": "L’attribut autocomplete doit être une valeur d’auto-remplissage valide",
|
|
40292
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.",
|
|
40293
40446
|
"autocompleteValid_summary_fail": "Cette valeur d’attribut autocomplete n’est pas une valeur d’auto-remplissage valide.",
|
|
@@ -44070,7 +44223,8 @@ const createDomHelpers = (function createDomHelpers(opts) {
|
|
|
44070
44223
|
'templateContent',
|
|
44071
44224
|
'nonRenderedElement',
|
|
44072
44225
|
'inputHidden',
|
|
44073
|
-
'visibilityHidden'
|
|
44226
|
+
'visibilityHidden',
|
|
44227
|
+
'contentVisibilityHidden'
|
|
44074
44228
|
]);
|
|
44075
44229
|
|
|
44076
44230
|
function queryAllSmart(sel) {
|
|
@@ -44087,6 +44241,23 @@ const createDomHelpers = (function createDomHelpers(opts) {
|
|
|
44087
44241
|
for (const r of reasons) {
|
|
44088
44242
|
if (HARD_HIDDEN_REASONS.has(r)) return false;
|
|
44089
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
|
+
}
|
|
44090
44261
|
return true;
|
|
44091
44262
|
} catch {
|
|
44092
44263
|
return true;
|
|
@@ -46663,6 +46834,36 @@ const createDomHelpers = (function createDomHelpers(opts) {
|
|
|
46663
46834
|
let node = el;
|
|
46664
46835
|
let safety = 0;
|
|
46665
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
|
+
|
|
46666
46867
|
while (node && node.nodeType === 1 && safety++ < 20) {
|
|
46667
46868
|
let anchor = null;
|
|
46668
46869
|
|
|
@@ -46702,7 +46903,7 @@ const createDomHelpers = (function createDomHelpers(opts) {
|
|
|
46702
46903
|
parts.unshift(nthOfType(node));
|
|
46703
46904
|
}
|
|
46704
46905
|
|
|
46705
|
-
if (!node.parentElement || roots.includes(node)) break;
|
|
46906
|
+
if (!node.parentElement || (stopAtMatchedRoot && roots.includes(node))) break;
|
|
46706
46907
|
node = node.parentElement;
|
|
46707
46908
|
}
|
|
46708
46909
|
|
|
@@ -46721,7 +46922,12 @@ const createDomHelpers = (function createDomHelpers(opts) {
|
|
|
46721
46922
|
// position relative to its own parent via `>` (child, not
|
|
46722
46923
|
// descendant) combinators, so a correctly-matching chain can
|
|
46723
46924
|
// only resolve to one element short of a malformed document
|
|
46724
|
-
// (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
|
|
46725
46931
|
// document-wide :nth-of-type scan was measured to cost O(total
|
|
46726
46932
|
// same-tag siblings) per call — pathological on pages with many
|
|
46727
46933
|
// flat, unidentified siblings (e.g. hundreds of unlabeled
|
|
@@ -47301,6 +47507,21 @@ const runCore = (function runCore(pageUrl, contextSelector, engineOptions, runOn
|
|
|
47301
47507
|
? engineOptionsResolved.timestamp.trim()
|
|
47302
47508
|
: null;
|
|
47303
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
|
+
|
|
47304
47525
|
const sharedHelpers = createDomHelpers({
|
|
47305
47526
|
document,
|
|
47306
47527
|
window,
|
|
@@ -53370,8 +53591,8 @@ const __a11yCoreCrossFrameApi = (function () {
|
|
|
53370
53591
|
},
|
|
53371
53592
|
{
|
|
53372
53593
|
"ruleId": "skip-link",
|
|
53373
|
-
"title": "Skip link must have a resolvable target",
|
|
53374
|
-
"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.",
|
|
53375
53596
|
"i18n": {
|
|
53376
53597
|
"titleKey": "skipLink_title",
|
|
53377
53598
|
"descriptionKey": "skipLink_description"
|
|
@@ -54958,13 +55179,7 @@ const __a11yCoreCrossFrameApi = (function () {
|
|
|
54958
55179
|
const RULE_IMPLS = {
|
|
54959
55180
|
"accesskeys": { run: (function runInPage(ctx) {
|
|
54960
55181
|
const { document, helpers, rule } = ctx;
|
|
54961
|
-
|
|
54962
|
-
let nodes = [];
|
|
54963
|
-
try {
|
|
54964
|
-
nodes = document.querySelectorAll('[accesskey]');
|
|
54965
|
-
} catch {
|
|
54966
|
-
nodes = [];
|
|
54967
|
-
}
|
|
55182
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[accesskey]') : helpers.queryAll('[accesskey]');
|
|
54968
55183
|
|
|
54969
55184
|
const groups = new Map(); // normalized key -> elements[]
|
|
54970
55185
|
for (const el of nodes) {
|
|
@@ -55555,8 +55770,7 @@ if (isAccTreeEligible) {
|
|
|
55555
55770
|
return { ruleId: rule.ruleId, outcome: 'cantTell', severity: 'minor', occurrences };
|
|
55556
55771
|
}), applicability: null },
|
|
55557
55772
|
"aria-allowed-attr": { run: (function runInPage(ctx) {
|
|
55558
|
-
const { document,
|
|
55559
|
-
const safeRoot = root || document;
|
|
55773
|
+
const { document, helpers, rule } = ctx;
|
|
55560
55774
|
|
|
55561
55775
|
const ariaHelpers = helpers && helpers.aria ? helpers.aria : null;
|
|
55562
55776
|
if (!ariaHelpers) {
|
|
@@ -55620,7 +55834,7 @@ if (isAccTreeEligible) {
|
|
|
55620
55834
|
};
|
|
55621
55835
|
|
|
55622
55836
|
const globalSet = new Set(GLOBAL_ATTRS);
|
|
55623
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role]'
|
|
55837
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role]') : helpers.queryAll('[role]');
|
|
55624
55838
|
|
|
55625
55839
|
const occurrences = [];
|
|
55626
55840
|
let applicableCount = 0;
|
|
@@ -55685,15 +55899,14 @@ if (isAccTreeEligible) {
|
|
|
55685
55899
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
55686
55900
|
}), applicability: null },
|
|
55687
55901
|
"aria-allowed-role": { run: (function runInPage(ctx) {
|
|
55688
|
-
const { document,
|
|
55689
|
-
const safeRoot = root || document;
|
|
55902
|
+
const { document, helpers, rule } = ctx;
|
|
55690
55903
|
|
|
55691
55904
|
const ariaHelpers = helpers && helpers.aria ? helpers.aria : null;
|
|
55692
55905
|
if (!ariaHelpers) {
|
|
55693
55906
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
55694
55907
|
}
|
|
55695
55908
|
|
|
55696
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role]'
|
|
55909
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role]') : helpers.queryAll('[role]');
|
|
55697
55910
|
|
|
55698
55911
|
const occurrences = [];
|
|
55699
55912
|
let applicableCount = 0;
|
|
@@ -55740,8 +55953,7 @@ if (isAccTreeEligible) {
|
|
|
55740
55953
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
55741
55954
|
}), applicability: null },
|
|
55742
55955
|
"aria-braille-equivalent": { run: (function runInPage(ctx) {
|
|
55743
|
-
const { document,
|
|
55744
|
-
const safeRoot = root || document;
|
|
55956
|
+
const { document, helpers, rule } = ctx;
|
|
55745
55957
|
|
|
55746
55958
|
function trim(v) { return (v == null ? '' : String(v)).trim(); }
|
|
55747
55959
|
|
|
@@ -55762,8 +55974,8 @@ if (isAccTreeEligible) {
|
|
|
55762
55974
|
}
|
|
55763
55975
|
|
|
55764
55976
|
const nodes = helpers.queryAllSmart
|
|
55765
|
-
? helpers.queryAllSmart('[aria-braillelabel], [aria-brailleroledescription]'
|
|
55766
|
-
: helpers.queryAll('[aria-braillelabel], [aria-brailleroledescription]'
|
|
55977
|
+
? helpers.queryAllSmart('[aria-braillelabel], [aria-brailleroledescription]')
|
|
55978
|
+
: helpers.queryAll('[aria-braillelabel], [aria-brailleroledescription]');
|
|
55767
55979
|
|
|
55768
55980
|
const occurrences = [];
|
|
55769
55981
|
let applicableCount = 0;
|
|
@@ -55824,14 +56036,13 @@ if (isAccTreeEligible) {
|
|
|
55824
56036
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
55825
56037
|
}), applicability: null },
|
|
55826
56038
|
"aria-checked-state-mismatch": { run: (function runInPage(ctx) {
|
|
55827
|
-
const { document,
|
|
55828
|
-
const safeRoot = root || document;
|
|
56039
|
+
const { document, helpers, rule } = ctx;
|
|
55829
56040
|
|
|
55830
56041
|
function trim(v) { return (v == null ? '' : String(v)).trim(); }
|
|
55831
56042
|
|
|
55832
56043
|
const nodes = helpers.queryAllSmart
|
|
55833
|
-
? helpers.queryAllSmart('input[type="checkbox"][aria-checked], input[type="radio"][aria-checked]'
|
|
55834
|
-
: 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]');
|
|
55835
56046
|
|
|
55836
56047
|
const occurrences = [];
|
|
55837
56048
|
let applicableCount = 0;
|
|
@@ -55893,16 +56104,15 @@ if (isAccTreeEligible) {
|
|
|
55893
56104
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
55894
56105
|
}), applicability: null },
|
|
55895
56106
|
"aria-conditional-attr": { run: (function runInPage(ctx) {
|
|
55896
|
-
const { document,
|
|
55897
|
-
const safeRoot = root || document;
|
|
56107
|
+
const { document, helpers, rule } = ctx;
|
|
55898
56108
|
|
|
55899
56109
|
function trim(v) { return (v == null ? '' : String(v)).trim(); }
|
|
55900
56110
|
|
|
55901
56111
|
const TRUTHY_INVALID_VALUES = new Set(['true', 'grammar', 'spelling']);
|
|
55902
56112
|
|
|
55903
56113
|
const nodes = helpers.queryAllSmart
|
|
55904
|
-
? helpers.queryAllSmart('[aria-errormessage]'
|
|
55905
|
-
: helpers.queryAll('[aria-errormessage]'
|
|
56114
|
+
? helpers.queryAllSmart('[aria-errormessage]')
|
|
56115
|
+
: helpers.queryAll('[aria-errormessage]');
|
|
55906
56116
|
|
|
55907
56117
|
const occurrences = [];
|
|
55908
56118
|
let applicableCount = 0;
|
|
@@ -55947,15 +56157,14 @@ if (isAccTreeEligible) {
|
|
|
55947
56157
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
55948
56158
|
}), applicability: null },
|
|
55949
56159
|
"aria-deprecated-role": { run: (function runInPage(ctx) {
|
|
55950
|
-
const { document,
|
|
55951
|
-
const safeRoot = root || document;
|
|
56160
|
+
const { document, helpers, rule } = ctx;
|
|
55952
56161
|
|
|
55953
56162
|
const ariaHelpers = helpers && helpers.aria ? helpers.aria : null;
|
|
55954
56163
|
if (!ariaHelpers) {
|
|
55955
56164
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
55956
56165
|
}
|
|
55957
56166
|
|
|
55958
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role]'
|
|
56167
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role]') : helpers.queryAll('[role]');
|
|
55959
56168
|
|
|
55960
56169
|
const occurrences = [];
|
|
55961
56170
|
let applicableCount = 0;
|
|
@@ -56467,8 +56676,7 @@ if (isAccTreeEligible) {
|
|
|
56467
56676
|
return { ruleId: rule.ruleId, outcome: 'fail', severity: rule.defaultSeverity || 'minor', occurrences };
|
|
56468
56677
|
}), applicability: null },
|
|
56469
56678
|
"aria-prohibited-attr": { run: (function runInPage(ctx) {
|
|
56470
|
-
const { document,
|
|
56471
|
-
const safeRoot = root || document;
|
|
56679
|
+
const { document, helpers, rule } = ctx;
|
|
56472
56680
|
|
|
56473
56681
|
const ariaHelpers = helpers && helpers.aria ? helpers.aria : null;
|
|
56474
56682
|
if (!ariaHelpers) {
|
|
@@ -56488,7 +56696,7 @@ if (isAccTreeEligible) {
|
|
|
56488
56696
|
|
|
56489
56697
|
const PROHIBITED_NAMING_ATTRS = ['aria-label', 'aria-labelledby'];
|
|
56490
56698
|
|
|
56491
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role]'
|
|
56699
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role]') : helpers.queryAll('[role]');
|
|
56492
56700
|
|
|
56493
56701
|
const occurrences = [];
|
|
56494
56702
|
let applicableCount = 0;
|
|
@@ -56539,8 +56747,7 @@ if (isAccTreeEligible) {
|
|
|
56539
56747
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
56540
56748
|
}), applicability: null },
|
|
56541
56749
|
"aria-prohibited-children": { run: (function runInPage(ctx) {
|
|
56542
|
-
const { document,
|
|
56543
|
-
const safeRoot = root || document;
|
|
56750
|
+
const { document, helpers, rule } = ctx;
|
|
56544
56751
|
|
|
56545
56752
|
const ariaHelpers = helpers && helpers.aria ? helpers.aria : null;
|
|
56546
56753
|
if (!ariaHelpers) {
|
|
@@ -56629,7 +56836,7 @@ if (isAccTreeEligible) {
|
|
|
56629
56836
|
}
|
|
56630
56837
|
}
|
|
56631
56838
|
|
|
56632
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role]'
|
|
56839
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role]') : helpers.queryAll('[role]');
|
|
56633
56840
|
|
|
56634
56841
|
const occurrences = [];
|
|
56635
56842
|
let applicableCount = 0;
|
|
@@ -56701,8 +56908,7 @@ if (isAccTreeEligible) {
|
|
|
56701
56908
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
56702
56909
|
}), applicability: null },
|
|
56703
56910
|
"aria-required-attr": { run: (function runInPage(ctx) {
|
|
56704
|
-
const { document,
|
|
56705
|
-
const safeRoot = root || document;
|
|
56911
|
+
const { document, helpers, rule } = ctx;
|
|
56706
56912
|
|
|
56707
56913
|
const ariaHelpers = helpers && helpers.aria ? helpers.aria : null;
|
|
56708
56914
|
if (!ariaHelpers) {
|
|
@@ -56726,7 +56932,7 @@ if (isAccTreeEligible) {
|
|
|
56726
56932
|
return v != null && String(v).trim().toLowerCase() === 'true';
|
|
56727
56933
|
}
|
|
56728
56934
|
|
|
56729
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role]'
|
|
56935
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role]') : helpers.queryAll('[role]');
|
|
56730
56936
|
|
|
56731
56937
|
const occurrences = [];
|
|
56732
56938
|
let applicableCount = 0;
|
|
@@ -56783,8 +56989,7 @@ if (isAccTreeEligible) {
|
|
|
56783
56989
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
56784
56990
|
}), applicability: null },
|
|
56785
56991
|
"aria-required-children": { run: (function runInPage(ctx) {
|
|
56786
|
-
const { document,
|
|
56787
|
-
const safeRoot = root || document;
|
|
56992
|
+
const { document, helpers, rule } = ctx;
|
|
56788
56993
|
|
|
56789
56994
|
const ariaHelpers = helpers && helpers.aria ? helpers.aria : null;
|
|
56790
56995
|
if (!ariaHelpers) {
|
|
@@ -56815,7 +57020,7 @@ if (isAccTreeEligible) {
|
|
|
56815
57020
|
// ("runInPage MUST be self-contained").
|
|
56816
57021
|
const CANDIDATE_SELECTOR = '[role], li, option, tr, td, th, thead, tbody, tfoot, ul, ol, table, select, input[type="radio"]';
|
|
56817
57022
|
|
|
56818
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role]'
|
|
57023
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role]') : helpers.queryAll('[role]');
|
|
56819
57024
|
|
|
56820
57025
|
const occurrences = [];
|
|
56821
57026
|
let applicableCount = 0;
|
|
@@ -56974,8 +57179,7 @@ if (isAccTreeEligible) {
|
|
|
56974
57179
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
56975
57180
|
}), applicability: null },
|
|
56976
57181
|
"aria-required-parent": { run: (function runInPage(ctx) {
|
|
56977
|
-
const { document,
|
|
56978
|
-
const safeRoot = root || document;
|
|
57182
|
+
const { document, helpers, rule } = ctx;
|
|
56979
57183
|
|
|
56980
57184
|
const ariaHelpers = helpers && helpers.aria ? helpers.aria : null;
|
|
56981
57185
|
if (!ariaHelpers) {
|
|
@@ -57061,7 +57265,7 @@ if (isAccTreeEligible) {
|
|
|
57061
57265
|
const idTok = elId && String(elId).trim();
|
|
57062
57266
|
if (!idTok) return false;
|
|
57063
57267
|
|
|
57064
|
-
const owners = helpers.queryAllSmart ? helpers.queryAllSmart('[aria-owns]'
|
|
57268
|
+
const owners = helpers.queryAllSmart ? helpers.queryAllSmart('[aria-owns]') : helpers.queryAll('[aria-owns]');
|
|
57065
57269
|
for (const owner of owners) {
|
|
57066
57270
|
if (!owner || !owner.getAttribute) continue;
|
|
57067
57271
|
const ownsAttr = owner.getAttribute('aria-owns') || '';
|
|
@@ -57074,7 +57278,7 @@ if (isAccTreeEligible) {
|
|
|
57074
57278
|
return false;
|
|
57075
57279
|
}
|
|
57076
57280
|
|
|
57077
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role]'
|
|
57281
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role]') : helpers.queryAll('[role]');
|
|
57078
57282
|
|
|
57079
57283
|
const occurrences = [];
|
|
57080
57284
|
let applicableCount = 0;
|
|
@@ -57287,15 +57491,14 @@ if (isAccTreeEligible) {
|
|
|
57287
57491
|
return { ruleId: rule.ruleId, outcome: 'fail', severity: rule.defaultSeverity || 'minor', occurrences };
|
|
57288
57492
|
}), applicability: null },
|
|
57289
57493
|
"aria-roles-valid": { run: (function runInPage(ctx) {
|
|
57290
|
-
const { document,
|
|
57291
|
-
const safeRoot = root || document;
|
|
57494
|
+
const { document, helpers, rule } = ctx;
|
|
57292
57495
|
|
|
57293
57496
|
const ariaHelpers = helpers && helpers.aria ? helpers.aria : null;
|
|
57294
57497
|
if (!ariaHelpers) {
|
|
57295
57498
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
57296
57499
|
}
|
|
57297
57500
|
|
|
57298
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role]'
|
|
57501
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role]') : helpers.queryAll('[role]');
|
|
57299
57502
|
|
|
57300
57503
|
const occurrences = [];
|
|
57301
57504
|
let applicableCount = 0;
|
|
@@ -57350,13 +57553,12 @@ if (isAccTreeEligible) {
|
|
|
57350
57553
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
57351
57554
|
}), applicability: null },
|
|
57352
57555
|
"aria-text": { run: (function runInPage(ctx) {
|
|
57353
|
-
const { document,
|
|
57354
|
-
const safeRoot = root || document;
|
|
57556
|
+
const { document, helpers, rule } = ctx;
|
|
57355
57557
|
|
|
57356
57558
|
const FOCUSABLE_DESCENDANT_SELECTOR =
|
|
57357
57559
|
'a[href], button, input, select, textarea, [tabindex], iframe, [contenteditable]:not([contenteditable="false"])';
|
|
57358
57560
|
|
|
57359
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role="text"]'
|
|
57561
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role="text"]') : helpers.queryAll('[role="text"]');
|
|
57360
57562
|
|
|
57361
57563
|
const occurrences = [];
|
|
57362
57564
|
let applicableCount = 0;
|
|
@@ -57410,15 +57612,14 @@ if (isAccTreeEligible) {
|
|
|
57410
57612
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
57411
57613
|
}), applicability: null },
|
|
57412
57614
|
"aria-valid-attr": { run: (function runInPage(ctx) {
|
|
57413
|
-
const { document,
|
|
57414
|
-
const safeRoot = root || document;
|
|
57615
|
+
const { document, helpers, rule } = ctx;
|
|
57415
57616
|
|
|
57416
57617
|
const ariaHelpers = helpers && helpers.aria ? helpers.aria : null;
|
|
57417
57618
|
if (!ariaHelpers) {
|
|
57418
57619
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
57419
57620
|
}
|
|
57420
57621
|
|
|
57421
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('*'
|
|
57622
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('*') : helpers.queryAll('*');
|
|
57422
57623
|
|
|
57423
57624
|
const occurrences = [];
|
|
57424
57625
|
let applicableCount = 0;
|
|
@@ -57472,15 +57673,14 @@ if (isAccTreeEligible) {
|
|
|
57472
57673
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
57473
57674
|
}), applicability: null },
|
|
57474
57675
|
"aria-valid-attr-value": { run: (function runInPage(ctx) {
|
|
57475
|
-
const { document,
|
|
57476
|
-
const safeRoot = root || document;
|
|
57676
|
+
const { document, helpers, rule } = ctx;
|
|
57477
57677
|
|
|
57478
57678
|
const ariaHelpers = helpers && helpers.aria ? helpers.aria : null;
|
|
57479
57679
|
if (!ariaHelpers) {
|
|
57480
57680
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
57481
57681
|
}
|
|
57482
57682
|
|
|
57483
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('*'
|
|
57683
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('*') : helpers.queryAll('*');
|
|
57484
57684
|
|
|
57485
57685
|
const occurrences = [];
|
|
57486
57686
|
let applicableCount = 0;
|
|
@@ -57537,8 +57737,7 @@ if (isAccTreeEligible) {
|
|
|
57537
57737
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
57538
57738
|
}), applicability: null },
|
|
57539
57739
|
"autocomplete-valid": { run: (function runInPage(ctx) {
|
|
57540
|
-
const { document,
|
|
57541
|
-
const safeRoot = root || document;
|
|
57740
|
+
const { document, helpers, rule } = ctx;
|
|
57542
57741
|
|
|
57543
57742
|
// Declared inside runInPage — see scripts/build-core.js header
|
|
57544
57743
|
// ("runInPage MUST be self-contained").
|
|
@@ -57573,7 +57772,7 @@ if (isAccTreeEligible) {
|
|
|
57573
57772
|
return FIELD_NAMES.has(remaining[0]);
|
|
57574
57773
|
}
|
|
57575
57774
|
|
|
57576
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('input, select, textarea'
|
|
57775
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('input, select, textarea') : helpers.queryAll('input, select, textarea');
|
|
57577
57776
|
|
|
57578
57777
|
const occurrences = [];
|
|
57579
57778
|
let applicableCount = 0;
|
|
@@ -57616,12 +57815,11 @@ if (isAccTreeEligible) {
|
|
|
57616
57815
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
57617
57816
|
}), applicability: null },
|
|
57618
57817
|
"avoid-inline-spacing": { run: (function runInPage(ctx) {
|
|
57619
|
-
const { document,
|
|
57620
|
-
const safeRoot = root || document;
|
|
57818
|
+
const { document, helpers, rule } = ctx;
|
|
57621
57819
|
|
|
57622
57820
|
const SPACING_PROPS = ['line-height', 'letter-spacing', 'word-spacing'];
|
|
57623
57821
|
|
|
57624
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[style]'
|
|
57822
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[style]') : helpers.queryAll('[style]');
|
|
57625
57823
|
|
|
57626
57824
|
const occurrences = [];
|
|
57627
57825
|
let applicableCount = 0;
|
|
@@ -57674,11 +57872,10 @@ if (isAccTreeEligible) {
|
|
|
57674
57872
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
57675
57873
|
}), applicability: null },
|
|
57676
57874
|
"binary-control-name-present": { run: (function runInPage(ctx) {
|
|
57677
|
-
const { document,
|
|
57875
|
+
const { document, helpers, rule } = ctx;
|
|
57678
57876
|
const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
|
|
57679
57877
|
? helpers.getEligibilityInfo
|
|
57680
57878
|
: null;
|
|
57681
|
-
const safeRoot = root || document;
|
|
57682
57879
|
|
|
57683
57880
|
|
|
57684
57881
|
function normalizeWs(s) {
|
|
@@ -57810,7 +58007,7 @@ if (isAccTreeEligible) {
|
|
|
57810
58007
|
let applicableCount = 0;
|
|
57811
58008
|
|
|
57812
58009
|
const selector = 'input[type="checkbox"], input[type="radio"], [role="checkbox"], [role="radio"], [role="switch"]';
|
|
57813
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
58010
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
57814
58011
|
|
|
57815
58012
|
|
|
57816
58013
|
// Precompute label[for] associations once for speed/determinism.
|
|
@@ -57942,8 +58139,7 @@ if (isAccTreeEligible) {
|
|
|
57942
58139
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
57943
58140
|
}), applicability: null },
|
|
57944
58141
|
"button-name-present": { run: (function runInPage(ctx) {
|
|
57945
|
-
const { document,
|
|
57946
|
-
const safeRoot = root || document;
|
|
58142
|
+
const { document, helpers, rule } = ctx;
|
|
57947
58143
|
|
|
57948
58144
|
const occurrences = [];
|
|
57949
58145
|
let applicableCount = 0;
|
|
@@ -57980,7 +58176,7 @@ if (isAccTreeEligible) {
|
|
|
57980
58176
|
}
|
|
57981
58177
|
|
|
57982
58178
|
const selector = 'button, input[type="button"], input[type="submit"], input[type="reset"], [role="button"]';
|
|
57983
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
58179
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
57984
58180
|
|
|
57985
58181
|
for (const el of nodes) {
|
|
57986
58182
|
// isAccTreeEligible returns { eligible, reasons }, not a boolean.
|
|
@@ -57992,6 +58188,7 @@ if (isAccTreeEligible) {
|
|
|
57992
58188
|
|
|
57993
58189
|
const tag = (el.tagName || '').toLowerCase();
|
|
57994
58190
|
const role = el.getAttribute ? el.getAttribute('role') : null;
|
|
58191
|
+
const roleNorm = normalizeWs(role).toLowerCase();
|
|
57995
58192
|
|
|
57996
58193
|
const nameInfo = helpers.getAccessibleNameInfo ? helpers.getAccessibleNameInfo(el, ctx) : null;
|
|
57997
58194
|
|
|
@@ -58007,7 +58204,17 @@ if (isAccTreeEligible) {
|
|
|
58007
58204
|
inputValueName = getInputButtonValueName(el);
|
|
58008
58205
|
}
|
|
58009
58206
|
|
|
58010
|
-
|
|
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);
|
|
58011
58218
|
const contentName =
|
|
58012
58219
|
(!trustedProgrammaticName && !inputValueName && isContentNameCandidate)
|
|
58013
58220
|
? getConservativeSubtreeText(el)
|
|
@@ -58067,14 +58274,43 @@ if (isAccTreeEligible) {
|
|
|
58067
58274
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
58068
58275
|
}
|
|
58069
58276
|
|
|
58070
|
-
function
|
|
58277
|
+
const isAccTreeEligible = helpers && typeof helpers.isAccTreeEligible === 'function' ? helpers.isAccTreeEligible : null;
|
|
58278
|
+
|
|
58279
|
+
function isExposedToAt(el) {
|
|
58280
|
+
if (!isAccTreeEligible) return true;
|
|
58071
58281
|
try {
|
|
58072
|
-
|
|
58282
|
+
const r = isAccTreeEligible(el, ctx);
|
|
58283
|
+
if (typeof r === 'boolean') return r;
|
|
58284
|
+
return !!(r && r.eligible);
|
|
58073
58285
|
} catch {
|
|
58074
|
-
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 [];
|
|
58075
58297
|
}
|
|
58076
58298
|
}
|
|
58077
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;
|
|
58310
|
+
}
|
|
58311
|
+
return false;
|
|
58312
|
+
}
|
|
58313
|
+
|
|
58078
58314
|
function hasWorkingAnchorLink() {
|
|
58079
58315
|
let links = [];
|
|
58080
58316
|
try {
|
|
@@ -58114,11 +58350,10 @@ if (isAccTreeEligible) {
|
|
|
58114
58350
|
}
|
|
58115
58351
|
|
|
58116
58352
|
function hasHeading() {
|
|
58117
|
-
|
|
58118
|
-
|
|
58119
|
-
} catch {
|
|
58120
|
-
return false;
|
|
58353
|
+
for (const el of queryAll('h1, h2, h3, h4, h5, h6, [role="heading"]')) {
|
|
58354
|
+
if (el && isExposedToAt(el)) return true;
|
|
58121
58355
|
}
|
|
58356
|
+
return false;
|
|
58122
58357
|
}
|
|
58123
58358
|
|
|
58124
58359
|
const mainLandmark = hasMainLandmark();
|
|
@@ -58376,11 +58611,10 @@ if (isAccTreeEligible) {
|
|
|
58376
58611
|
return { ruleId: rule.ruleId, outcome: 'cantTell', severity: 'minor', occurrences };
|
|
58377
58612
|
}), applicability: null },
|
|
58378
58613
|
"combobox-name-present": { run: (function runInPage(ctx) {
|
|
58379
|
-
const { document,
|
|
58614
|
+
const { document, helpers, rule } = ctx;
|
|
58380
58615
|
const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
|
|
58381
58616
|
? helpers.getEligibilityInfo
|
|
58382
58617
|
: null;
|
|
58383
|
-
const safeRoot = root || document;
|
|
58384
58618
|
|
|
58385
58619
|
|
|
58386
58620
|
function normalizeWs(s) {
|
|
@@ -58512,7 +58746,7 @@ if (isAccTreeEligible) {
|
|
|
58512
58746
|
let applicableCount = 0;
|
|
58513
58747
|
|
|
58514
58748
|
const selector = '[role="combobox"]';
|
|
58515
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
58749
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
58516
58750
|
|
|
58517
58751
|
// Precompute label[for] map for combobox elements that are labelable
|
|
58518
58752
|
// native form controls (e.g. <input role="combobox">).
|
|
@@ -60239,14 +60473,13 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
60239
60473
|
return { ruleId: rule.ruleId, outcome: 'fail', severity: rule.defaultSeverity || 'serious', occurrences };
|
|
60240
60474
|
}), applicability: null },
|
|
60241
60475
|
"definition-list-children-valid": { run: (function runInPage(ctx) {
|
|
60242
|
-
const { document,
|
|
60243
|
-
const safeRoot = root || document;
|
|
60476
|
+
const { document, helpers, rule } = ctx;
|
|
60244
60477
|
|
|
60245
60478
|
// Declared inside runInPage — see scripts/build-core.js header
|
|
60246
60479
|
// ("runInPage MUST be self-contained").
|
|
60247
60480
|
const PASSTHROUGH_TAGS = new Set(['dt', 'dd', 'script', 'template', 'style']);
|
|
60248
60481
|
|
|
60249
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('dl'
|
|
60482
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('dl') : helpers.queryAll('dl');
|
|
60250
60483
|
|
|
60251
60484
|
const occurrences = [];
|
|
60252
60485
|
let applicableCount = 0;
|
|
@@ -60330,10 +60563,9 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
60330
60563
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
60331
60564
|
}), applicability: null },
|
|
60332
60565
|
"deprecated-elements-not-used": { run: (function runInPage(ctx) {
|
|
60333
|
-
const { document,
|
|
60334
|
-
const safeRoot = root || document;
|
|
60566
|
+
const { document, helpers, rule } = ctx;
|
|
60335
60567
|
|
|
60336
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('blink, marquee'
|
|
60568
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('blink, marquee') : helpers.queryAll('blink, marquee');
|
|
60337
60569
|
|
|
60338
60570
|
const occurrences = [];
|
|
60339
60571
|
let applicableCount = 0;
|
|
@@ -60372,11 +60604,10 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
60372
60604
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
60373
60605
|
}), applicability: null },
|
|
60374
60606
|
"dialog-name-present": { run: (function runInPage(ctx) {
|
|
60375
|
-
const { document,
|
|
60607
|
+
const { document, helpers, rule } = ctx;
|
|
60376
60608
|
const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
|
|
60377
60609
|
? helpers.getEligibilityInfo
|
|
60378
60610
|
: null;
|
|
60379
|
-
const safeRoot = root || document;
|
|
60380
60611
|
|
|
60381
60612
|
|
|
60382
60613
|
function normalizeWs(s) {
|
|
@@ -60490,7 +60721,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
60490
60721
|
let applicableCount = 0;
|
|
60491
60722
|
|
|
60492
60723
|
const selector = '[role="dialog"],[role="alertdialog"]';
|
|
60493
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
60724
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
60494
60725
|
|
|
60495
60726
|
|
|
60496
60727
|
function evaluate(el) {
|
|
@@ -60555,10 +60786,9 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
60555
60786
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
60556
60787
|
}), applicability: null },
|
|
60557
60788
|
"dlitem-parent-valid": { run: (function runInPage(ctx) {
|
|
60558
|
-
const { document,
|
|
60559
|
-
const safeRoot = root || document;
|
|
60789
|
+
const { document, helpers, rule } = ctx;
|
|
60560
60790
|
|
|
60561
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('dt, dd'
|
|
60791
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('dt, dd') : helpers.queryAll('dt, dd');
|
|
60562
60792
|
|
|
60563
60793
|
const occurrences = [];
|
|
60564
60794
|
let applicableCount = 0;
|
|
@@ -61210,8 +61440,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
61210
61440
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
61211
61441
|
}), applicability: null },
|
|
61212
61442
|
"focus-order-semantics": { run: (function runInPage(ctx) {
|
|
61213
|
-
const { document,
|
|
61214
|
-
const safeRoot = root || document;
|
|
61443
|
+
const { document, helpers, rule } = ctx;
|
|
61215
61444
|
|
|
61216
61445
|
const NON_INTERACTIVE_ROLES = new Set([
|
|
61217
61446
|
'generic', 'group', 'text', 'presentation', 'none', 'img', 'heading',
|
|
@@ -61221,7 +61450,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
61221
61450
|
'definition', 'paragraph', 'caption', 'status', 'alert', 'log', 'tooltip'
|
|
61222
61451
|
]);
|
|
61223
61452
|
|
|
61224
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[tabindex][role]'
|
|
61453
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[tabindex][role]') : helpers.queryAll('[tabindex][role]');
|
|
61225
61454
|
|
|
61226
61455
|
const occurrences = [];
|
|
61227
61456
|
let applicableCount = 0;
|
|
@@ -61862,11 +62091,10 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
61862
62091
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [], data: { details: { metrics } } };
|
|
61863
62092
|
}), applicability: null },
|
|
61864
62093
|
"form-control-single-label": { run: (function runInPage(ctx) {
|
|
61865
|
-
const { document,
|
|
61866
|
-
const safeRoot = root || document;
|
|
62094
|
+
const { document, helpers, rule } = ctx;
|
|
61867
62095
|
|
|
61868
62096
|
const selector = 'input:not([type="hidden"]):not([type="submit"]):not([type="reset"]):not([type="button"]):not([type="image"]),select,textarea';
|
|
61869
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
62097
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
61870
62098
|
|
|
61871
62099
|
// Build a for-value -> label[] map once (avoids per-control dynamic
|
|
61872
62100
|
// attribute-selector construction / CSS.escape, which is not guaranteed
|
|
@@ -62144,14 +62372,13 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
62144
62372
|
return { ruleId: rule.ruleId, outcome: 'fail', severity: rule.defaultSeverity || 'serious', occurrences };
|
|
62145
62373
|
}), applicability: null },
|
|
62146
62374
|
"identical-links-same-purpose": { run: (function runInPage(ctx) {
|
|
62147
|
-
const { document,
|
|
62148
|
-
const safeRoot = root || document;
|
|
62375
|
+
const { document, helpers, rule } = ctx;
|
|
62149
62376
|
|
|
62150
62377
|
function normName(s) {
|
|
62151
62378
|
return (s == null ? '' : String(s)).replace(/\s+/g, ' ').trim().toLowerCase();
|
|
62152
62379
|
}
|
|
62153
62380
|
|
|
62154
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('a[href]'
|
|
62381
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('a[href]') : helpers.queryAll('a[href]');
|
|
62155
62382
|
|
|
62156
62383
|
const groups = new Map(); // normName -> [{ el, href }]
|
|
62157
62384
|
let applicableCount = 0;
|
|
@@ -62231,8 +62458,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
62231
62458
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
62232
62459
|
}), applicability: null },
|
|
62233
62460
|
"iframe-focusable-content": { run: (function runInPage(ctx) {
|
|
62234
|
-
const { document,
|
|
62235
|
-
const safeRoot = root || document;
|
|
62461
|
+
const { document, helpers, rule } = ctx;
|
|
62236
62462
|
|
|
62237
62463
|
function hasFocusableCandidate(doc) {
|
|
62238
62464
|
if (!doc || !doc.querySelectorAll) return false;
|
|
@@ -62264,7 +62490,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
62264
62490
|
return !Number.isNaN(n) && n < 0;
|
|
62265
62491
|
}
|
|
62266
62492
|
|
|
62267
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('iframe, frame'
|
|
62493
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('iframe, frame') : helpers.queryAll('iframe, frame');
|
|
62268
62494
|
|
|
62269
62495
|
const occurrences = [];
|
|
62270
62496
|
let applicableCount = 0;
|
|
@@ -62314,10 +62540,9 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
62314
62540
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
62315
62541
|
}), applicability: null },
|
|
62316
62542
|
"iframe-name-present": { run: (function runInPage(ctx) {
|
|
62317
|
-
const { document,
|
|
62318
|
-
const safeRoot = root || document;
|
|
62543
|
+
const { document, helpers, rule } = ctx;
|
|
62319
62544
|
|
|
62320
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('iframe, frame'
|
|
62545
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('iframe, frame') : helpers.queryAll('iframe, frame');
|
|
62321
62546
|
|
|
62322
62547
|
const occurrences = [];
|
|
62323
62548
|
let applicableCount = 0;
|
|
@@ -62369,10 +62594,9 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
62369
62594
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
62370
62595
|
}), applicability: null },
|
|
62371
62596
|
"iframe-title-unique": { run: (function runInPage(ctx) {
|
|
62372
|
-
const { document,
|
|
62373
|
-
const safeRoot = root || document;
|
|
62597
|
+
const { document, helpers, rule } = ctx;
|
|
62374
62598
|
|
|
62375
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('iframe, frame'
|
|
62599
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('iframe, frame') : helpers.queryAll('iframe, frame');
|
|
62376
62600
|
|
|
62377
62601
|
const groups = new Map(); // trimmed title -> elements[]
|
|
62378
62602
|
let applicableCount = 0;
|
|
@@ -62427,8 +62651,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
62427
62651
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
62428
62652
|
}), applicability: null },
|
|
62429
62653
|
"image-redundant-alt": { run: (function runInPage(ctx) {
|
|
62430
|
-
const { document,
|
|
62431
|
-
const safeRoot = root || document;
|
|
62654
|
+
const { document, helpers, rule } = ctx;
|
|
62432
62655
|
|
|
62433
62656
|
function normalizeWs(s) {
|
|
62434
62657
|
return String(s || '').replace(/\s+/g, ' ').trim();
|
|
@@ -62447,7 +62670,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
62447
62670
|
return normalizeWs(text);
|
|
62448
62671
|
}
|
|
62449
62672
|
|
|
62450
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('img[alt]'
|
|
62673
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('img[alt]') : helpers.queryAll('img[alt]');
|
|
62451
62674
|
|
|
62452
62675
|
const occurrences = [];
|
|
62453
62676
|
let applicableCount = 0;
|
|
@@ -63235,8 +63458,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
63235
63458
|
return { ruleId: rule.ruleId, outcome: 'cantTell', severity: 'minor', occurrences };
|
|
63236
63459
|
}), applicability: null },
|
|
63237
63460
|
"label-in-name": { run: (function runInPage(ctx) {
|
|
63238
|
-
const { document,
|
|
63239
|
-
const safeRoot = root || document;
|
|
63461
|
+
const { document, helpers, rule } = ctx;
|
|
63240
63462
|
|
|
63241
63463
|
const occurrences = [];
|
|
63242
63464
|
let applicableCount = 0;
|
|
@@ -63245,7 +63467,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
63245
63467
|
// NOTE: aria-hidden is intentionally NOT excluded here; it does not affect visual rendering.
|
|
63246
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])';
|
|
63247
63469
|
|
|
63248
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
63470
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
63249
63471
|
|
|
63250
63472
|
function norm(s) {
|
|
63251
63473
|
const v = (s == null ? '' : String(s));
|
|
@@ -63383,11 +63605,11 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
63383
63605
|
}
|
|
63384
63606
|
|
|
63385
63607
|
// Fallback: label[for=id]. Uses `document` directly rather than
|
|
63386
|
-
// ctx.root
|
|
63387
|
-
//
|
|
63388
|
-
//
|
|
63389
|
-
//
|
|
63390
|
-
//
|
|
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.
|
|
63391
63613
|
try {
|
|
63392
63614
|
const idAttribute = control && control.getAttribute ? (control.getAttribute('id') || '') : '';
|
|
63393
63615
|
const key = String(idAttribute || '').trim();
|
|
@@ -63528,11 +63750,10 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
63528
63750
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
63529
63751
|
}), applicability: null },
|
|
63530
63752
|
"label-title-only": { run: (function runInPage(ctx) {
|
|
63531
|
-
const { document,
|
|
63532
|
-
const safeRoot = root || document;
|
|
63753
|
+
const { document, helpers, rule } = ctx;
|
|
63533
63754
|
|
|
63534
63755
|
const selector = 'input:not([type="hidden"]):not([type="submit"]):not([type="reset"]):not([type="button"]):not([type="image"]),select,textarea';
|
|
63535
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
63756
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
63536
63757
|
|
|
63537
63758
|
const labelsByFor = new Map();
|
|
63538
63759
|
const allLabels = document.getElementsByTagName ? document.getElementsByTagName('label') : [];
|
|
@@ -64579,8 +64800,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
64579
64800
|
return { ruleId: rule.ruleId, outcome: 'cantTell', severity: rule.defaultSeverity || 'minor', occurrences };
|
|
64580
64801
|
}), applicability: null },
|
|
64581
64802
|
"link-in-text-block": { run: (function runInPage(ctx) {
|
|
64582
|
-
const { document,
|
|
64583
|
-
const safeRoot = root || document;
|
|
64803
|
+
const { document, helpers, rule, engineOptions } = ctx;
|
|
64584
64804
|
|
|
64585
64805
|
function safeComputedStyle(el) {
|
|
64586
64806
|
try {
|
|
@@ -64626,7 +64846,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
64626
64846
|
const c = helpers && helpers.contrast ? helpers.contrast : null;
|
|
64627
64847
|
|
|
64628
64848
|
const selector = 'a[href]';
|
|
64629
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
64849
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
64630
64850
|
|
|
64631
64851
|
const occurrences = [];
|
|
64632
64852
|
let applicableCount = 0;
|
|
@@ -64726,8 +64946,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
64726
64946
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
64727
64947
|
}), applicability: null },
|
|
64728
64948
|
"link-name-present": { run: (function runInPage(ctx) {
|
|
64729
|
-
const { document,
|
|
64730
|
-
const safeRoot = root || document;
|
|
64949
|
+
const { document, helpers, rule } = ctx;
|
|
64731
64950
|
|
|
64732
64951
|
const occurrences = [];
|
|
64733
64952
|
let applicableCount = 0;
|
|
@@ -64748,7 +64967,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
64748
64967
|
}
|
|
64749
64968
|
|
|
64750
64969
|
const selector = 'a[href], area[href], [role="link"]';
|
|
64751
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
64970
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
64752
64971
|
|
|
64753
64972
|
for (const el of nodes) {
|
|
64754
64973
|
// isAccTreeEligible returns { eligible, reasons }, not a boolean.
|
|
@@ -64761,7 +64980,20 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
64761
64980
|
const nameInfo = helpers.getAccessibleNameInfo ? helpers.getAccessibleNameInfo(el, ctx) : null;
|
|
64762
64981
|
const programmaticName = (nameInfo && typeof nameInfo.value === 'string') ? nameInfo.value : '';
|
|
64763
64982
|
|
|
64764
|
-
|
|
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) : '';
|
|
64765
64997
|
const finalName = (programmaticName.trim().length ? programmaticName : contentName).trim();
|
|
64766
64998
|
|
|
64767
64999
|
if (finalName.length === 0) {
|
|
@@ -64813,8 +65045,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
64813
65045
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
64814
65046
|
}), applicability: null },
|
|
64815
65047
|
"link-name-quality": { run: (function runInPage(ctx) {
|
|
64816
|
-
const { document,
|
|
64817
|
-
const safeRoot = root || document;
|
|
65048
|
+
const { document, helpers, rule } = ctx;
|
|
64818
65049
|
|
|
64819
65050
|
const GENERIC_LINK_TEXT = new Set([
|
|
64820
65051
|
'click here', 'here', 'click', 'more', 'more info', 'more information',
|
|
@@ -64833,7 +65064,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
64833
65064
|
}
|
|
64834
65065
|
|
|
64835
65066
|
const selector = 'a[href]';
|
|
64836
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
65067
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
64837
65068
|
|
|
64838
65069
|
const occurrences = [];
|
|
64839
65070
|
let applicableCount = 0;
|
|
@@ -64893,14 +65124,13 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
64893
65124
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
64894
65125
|
}), applicability: null },
|
|
64895
65126
|
"list-children-valid": { run: (function runInPage(ctx) {
|
|
64896
|
-
const { document,
|
|
64897
|
-
const safeRoot = root || document;
|
|
65127
|
+
const { document, helpers, rule } = ctx;
|
|
64898
65128
|
|
|
64899
65129
|
// Declared inside runInPage — see scripts/build-core.js header
|
|
64900
65130
|
// ("runInPage MUST be self-contained").
|
|
64901
65131
|
const ALLOWED_CHILD_TAGS = new Set(['li', 'script', 'template']);
|
|
64902
65132
|
|
|
64903
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('ul, ol'
|
|
65133
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('ul, ol') : helpers.queryAll('ul, ol');
|
|
64904
65134
|
|
|
64905
65135
|
const isAccTreeEligible = helpers && typeof helpers.isAccTreeEligible === 'function' ? helpers.isAccTreeEligible : null;
|
|
64906
65136
|
|
|
@@ -64971,11 +65201,10 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
64971
65201
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
64972
65202
|
}), applicability: null },
|
|
64973
65203
|
"listbox-name-present": { run: (function runInPage(ctx) {
|
|
64974
|
-
const { document,
|
|
65204
|
+
const { document, helpers, rule } = ctx;
|
|
64975
65205
|
const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
|
|
64976
65206
|
? helpers.getEligibilityInfo
|
|
64977
65207
|
: null;
|
|
64978
|
-
const safeRoot = root || document;
|
|
64979
65208
|
|
|
64980
65209
|
|
|
64981
65210
|
function normalizeWs(s) {
|
|
@@ -65080,7 +65309,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
65080
65309
|
let applicableCount = 0;
|
|
65081
65310
|
|
|
65082
65311
|
const selector = "[role=\"listbox\"]";
|
|
65083
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
65312
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
65084
65313
|
|
|
65085
65314
|
// Precompute label[for] map for listbox elements that are labelable
|
|
65086
65315
|
// native form controls (e.g. <select multiple role="listbox">).
|
|
@@ -65181,10 +65410,9 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
65181
65410
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
65182
65411
|
}), applicability: null },
|
|
65183
65412
|
"listitem-parent-valid": { run: (function runInPage(ctx) {
|
|
65184
|
-
const { document,
|
|
65185
|
-
const safeRoot = root || document;
|
|
65413
|
+
const { document, helpers, rule } = ctx;
|
|
65186
65414
|
|
|
65187
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('li'
|
|
65415
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('li') : helpers.queryAll('li');
|
|
65188
65416
|
|
|
65189
65417
|
const occurrences = [];
|
|
65190
65418
|
let applicableCount = 0;
|
|
@@ -65613,11 +65841,10 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
65613
65841
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
65614
65842
|
}), applicability: null },
|
|
65615
65843
|
"menuitem-name-present": { run: (function runInPage(ctx) {
|
|
65616
|
-
const { document,
|
|
65844
|
+
const { document, helpers, rule } = ctx;
|
|
65617
65845
|
const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
|
|
65618
65846
|
? helpers.getEligibilityInfo
|
|
65619
65847
|
: null;
|
|
65620
|
-
const safeRoot = root || document;
|
|
65621
65848
|
|
|
65622
65849
|
|
|
65623
65850
|
function normalizeWs(s) {
|
|
@@ -65723,7 +65950,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
65723
65950
|
let applicableCount = 0;
|
|
65724
65951
|
|
|
65725
65952
|
const selector = '[role="menuitem"],[role="menuitemcheckbox"],[role="menuitemradio"]';
|
|
65726
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
65953
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
65727
65954
|
|
|
65728
65955
|
|
|
65729
65956
|
function evaluate(el) {
|
|
@@ -66029,11 +66256,10 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
66029
66256
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
66030
66257
|
}), applicability: null },
|
|
66031
66258
|
"meter-name-present": { run: (function runInPage(ctx) {
|
|
66032
|
-
const { document,
|
|
66259
|
+
const { document, helpers, rule } = ctx;
|
|
66033
66260
|
const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
|
|
66034
66261
|
? helpers.getEligibilityInfo
|
|
66035
66262
|
: null;
|
|
66036
|
-
const safeRoot = root || document;
|
|
66037
66263
|
|
|
66038
66264
|
|
|
66039
66265
|
function normalizeWs(s) {
|
|
@@ -66097,7 +66323,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
66097
66323
|
let applicableCount = 0;
|
|
66098
66324
|
|
|
66099
66325
|
const selector = '[role="meter"]';
|
|
66100
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
66326
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
66101
66327
|
|
|
66102
66328
|
|
|
66103
66329
|
function evaluate(el) {
|
|
@@ -66163,8 +66389,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
66163
66389
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
66164
66390
|
}), applicability: null },
|
|
66165
66391
|
"mouse-only-event-handlers": { run: (function runInPage(ctx) {
|
|
66166
|
-
const { document,
|
|
66167
|
-
const safeRoot = root || document;
|
|
66392
|
+
const { document, helpers, rule } = ctx;
|
|
66168
66393
|
|
|
66169
66394
|
const MOUSE_ONLY_ATTRS = [
|
|
66170
66395
|
'onmouseover', 'onmouseout', 'onmousedown', 'onmouseup',
|
|
@@ -66175,7 +66400,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
66175
66400
|
function trim(v) { return (v == null ? '' : String(v)).trim(); }
|
|
66176
66401
|
|
|
66177
66402
|
const selector = MOUSE_ONLY_ATTRS.map((a) => `[${a}]`).join(', ');
|
|
66178
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
66403
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
66179
66404
|
|
|
66180
66405
|
const occurrences = [];
|
|
66181
66406
|
let applicableCount = 0;
|
|
@@ -66227,8 +66452,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
66227
66452
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
66228
66453
|
}), applicability: null },
|
|
66229
66454
|
"nested-interactive-controls-absent": { run: (function runInPage(ctx) {
|
|
66230
|
-
const { document,
|
|
66231
|
-
const safeRoot = root || document;
|
|
66455
|
+
const { document, helpers, rule } = ctx;
|
|
66232
66456
|
|
|
66233
66457
|
// Declared inside runInPage — see scripts/build-core.js header
|
|
66234
66458
|
// ("runInPage MUST be self-contained").
|
|
@@ -66257,7 +66481,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
66257
66481
|
'[role="treeitem"]'
|
|
66258
66482
|
].join(', ');
|
|
66259
66483
|
|
|
66260
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(INTERACTIVE_SELECTOR
|
|
66484
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(INTERACTIVE_SELECTOR) : helpers.queryAll(INTERACTIVE_SELECTOR);
|
|
66261
66485
|
|
|
66262
66486
|
const occurrences = [];
|
|
66263
66487
|
let applicableCount = 0;
|
|
@@ -66306,10 +66530,9 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
66306
66530
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
66307
66531
|
}), applicability: null },
|
|
66308
66532
|
"no-autoplay-audio": { run: (function runInPage(ctx) {
|
|
66309
|
-
const { document,
|
|
66310
|
-
const safeRoot = root || document;
|
|
66533
|
+
const { document, helpers, rule } = ctx;
|
|
66311
66534
|
|
|
66312
|
-
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]');
|
|
66313
66536
|
|
|
66314
66537
|
const occurrences = [];
|
|
66315
66538
|
let applicableCount = 0;
|
|
@@ -66663,11 +66886,10 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
66663
66886
|
return {ruleId: rule.ruleId, outcome: 'cantTell', severity: 'minor', occurrences};
|
|
66664
66887
|
}), applicability: null },
|
|
66665
66888
|
"option-name-present": { run: (function runInPage(ctx) {
|
|
66666
|
-
const { document,
|
|
66889
|
+
const { document, helpers, rule } = ctx;
|
|
66667
66890
|
const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
|
|
66668
66891
|
? helpers.getEligibilityInfo
|
|
66669
66892
|
: null;
|
|
66670
|
-
const safeRoot = root || document;
|
|
66671
66893
|
|
|
66672
66894
|
|
|
66673
66895
|
function normalizeWs(s) {
|
|
@@ -66731,7 +66953,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
66731
66953
|
let applicableCount = 0;
|
|
66732
66954
|
|
|
66733
66955
|
const selector = "[role=\"option\"]";
|
|
66734
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
66956
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
66735
66957
|
|
|
66736
66958
|
|
|
66737
66959
|
function hasName(el) {
|
|
@@ -66794,8 +67016,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
66794
67016
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
66795
67017
|
}), applicability: null },
|
|
66796
67018
|
"p-as-heading": { run: (function runInPage(ctx) {
|
|
66797
|
-
const { document,
|
|
66798
|
-
const safeRoot = root || document;
|
|
67019
|
+
const { document, helpers, rule } = ctx;
|
|
66799
67020
|
|
|
66800
67021
|
const MAX_HEADING_LIKE_CHARS = 120;
|
|
66801
67022
|
const MIN_FONT_SIZE_PX = 18;
|
|
@@ -66847,7 +67068,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
66847
67068
|
return Number.isFinite(px) ? px : 0;
|
|
66848
67069
|
}
|
|
66849
67070
|
|
|
66850
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('p'
|
|
67071
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('p') : helpers.queryAll('p');
|
|
66851
67072
|
|
|
66852
67073
|
const occurrences = [];
|
|
66853
67074
|
let applicableCount = 0;
|
|
@@ -66929,14 +67150,31 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
66929
67150
|
return tag === 'h1';
|
|
66930
67151
|
}
|
|
66931
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.
|
|
66932
67168
|
let nodes = [];
|
|
66933
67169
|
try {
|
|
66934
|
-
nodes =
|
|
67170
|
+
nodes = helpers && typeof helpers.queryAllSmart === 'function'
|
|
67171
|
+
? helpers.queryAllSmart('h1, [role]')
|
|
67172
|
+
: document.querySelectorAll('h1, [role]');
|
|
66935
67173
|
} catch {
|
|
66936
67174
|
nodes = [];
|
|
66937
67175
|
}
|
|
66938
67176
|
|
|
66939
|
-
const hasH1 = Array.from(nodes).some((el) => el && isLevelOneHeading(el));
|
|
67177
|
+
const hasH1 = Array.from(nodes).some((el) => el && isLevelOneHeading(el) && isExposedToAt(el));
|
|
66940
67178
|
|
|
66941
67179
|
if (hasH1) {
|
|
66942
67180
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
@@ -67260,8 +67498,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
67260
67498
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
67261
67499
|
}), applicability: null },
|
|
67262
67500
|
"presentation-role-conflict": { run: (function runInPage(ctx) {
|
|
67263
|
-
const { document,
|
|
67264
|
-
const safeRoot = root || document;
|
|
67501
|
+
const { document, helpers, rule } = ctx;
|
|
67265
67502
|
|
|
67266
67503
|
// The full set of ARIA attributes marked `global: true` per the WAI-ARIA
|
|
67267
67504
|
// spec (confirmed against a widely-used reference engine's own
|
|
@@ -67278,8 +67515,8 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
67278
67515
|
const getFocusableInfo = helpers && typeof helpers.getFocusableInfo === 'function' ? helpers.getFocusableInfo : null;
|
|
67279
67516
|
|
|
67280
67517
|
const nodes = helpers.queryAllSmart
|
|
67281
|
-
? helpers.queryAllSmart('[role="presentation"], [role="none"], img[alt=""]'
|
|
67282
|
-
: 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=""]');
|
|
67283
67520
|
|
|
67284
67521
|
const occurrences = [];
|
|
67285
67522
|
let applicableCount = 0;
|
|
@@ -67343,11 +67580,10 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
67343
67580
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
67344
67581
|
}), applicability: null },
|
|
67345
67582
|
"progressbar-name-present": { run: (function runInPage(ctx) {
|
|
67346
|
-
const { document,
|
|
67583
|
+
const { document, helpers, rule } = ctx;
|
|
67347
67584
|
const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
|
|
67348
67585
|
? helpers.getEligibilityInfo
|
|
67349
67586
|
: null;
|
|
67350
|
-
const safeRoot = root || document;
|
|
67351
67587
|
|
|
67352
67588
|
|
|
67353
67589
|
function normalizeWs(s) {
|
|
@@ -67411,7 +67647,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
67411
67647
|
let applicableCount = 0;
|
|
67412
67648
|
|
|
67413
67649
|
const selector = '[role="progressbar"]';
|
|
67414
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
67650
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
67415
67651
|
|
|
67416
67652
|
|
|
67417
67653
|
function evaluate(el) {
|
|
@@ -67823,8 +68059,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
67823
68059
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
67824
68060
|
}), applicability: null },
|
|
67825
68061
|
"scrollable-region-focusable": { run: (function runInPage(ctx) {
|
|
67826
|
-
const { document,
|
|
67827
|
-
const safeRoot = root || document;
|
|
68062
|
+
const { document, helpers, rule } = ctx;
|
|
67828
68063
|
|
|
67829
68064
|
function safeComputedStyle(el) {
|
|
67830
68065
|
try {
|
|
@@ -67869,7 +68104,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
67869
68104
|
}
|
|
67870
68105
|
|
|
67871
68106
|
const CANDIDATE_SELECTOR = 'div, section, article, aside, main, nav, pre, table, blockquote, ul, ol, textarea';
|
|
67872
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(CANDIDATE_SELECTOR
|
|
68107
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(CANDIDATE_SELECTOR) : helpers.queryAll(CANDIDATE_SELECTOR);
|
|
67873
68108
|
|
|
67874
68109
|
const occurrences = [];
|
|
67875
68110
|
let applicableCount = 0;
|
|
@@ -67924,11 +68159,10 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
67924
68159
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
67925
68160
|
}), applicability: null },
|
|
67926
68161
|
"searchbox-name-present": { run: (function runInPage(ctx) {
|
|
67927
|
-
const { document,
|
|
68162
|
+
const { document, helpers, rule } = ctx;
|
|
67928
68163
|
const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
|
|
67929
68164
|
? helpers.getEligibilityInfo
|
|
67930
68165
|
: null;
|
|
67931
|
-
const safeRoot = root || document;
|
|
67932
68166
|
|
|
67933
68167
|
|
|
67934
68168
|
function normalizeWs(s) {
|
|
@@ -68033,7 +68267,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
68033
68267
|
let applicableCount = 0;
|
|
68034
68268
|
|
|
68035
68269
|
const selector = "[role=\"searchbox\"]";
|
|
68036
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
68270
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
68037
68271
|
|
|
68038
68272
|
// Precompute label[for] map for searchbox elements that are labelable
|
|
68039
68273
|
// native form controls (e.g. <input role="searchbox">).
|
|
@@ -68134,10 +68368,9 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
68134
68368
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
68135
68369
|
}), applicability: null },
|
|
68136
68370
|
"server-side-image-map-absent": { run: (function runInPage(ctx) {
|
|
68137
|
-
const { document,
|
|
68138
|
-
const safeRoot = root || document;
|
|
68371
|
+
const { document, helpers, rule } = ctx;
|
|
68139
68372
|
|
|
68140
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('img[ismap]'
|
|
68373
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('img[ismap]') : helpers.queryAll('img[ismap]');
|
|
68141
68374
|
|
|
68142
68375
|
const occurrences = [];
|
|
68143
68376
|
let applicableCount = 0;
|
|
@@ -68175,8 +68408,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
68175
68408
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
68176
68409
|
}), applicability: null },
|
|
68177
68410
|
"skip-link": { run: (function runInPage(ctx) {
|
|
68178
|
-
const { document,
|
|
68179
|
-
const safeRoot = root || document;
|
|
68411
|
+
const { document, helpers, rule } = ctx;
|
|
68180
68412
|
|
|
68181
68413
|
function normalizeWs(s) {
|
|
68182
68414
|
return String(s || '').replace(/\s+/g, ' ').trim();
|
|
@@ -68203,7 +68435,31 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
68203
68435
|
return normalizeWs(el.textContent);
|
|
68204
68436
|
}
|
|
68205
68437
|
|
|
68206
|
-
|
|
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]');
|
|
68207
68463
|
|
|
68208
68464
|
const occurrences = [];
|
|
68209
68465
|
let applicableCount = 0;
|
|
@@ -68241,7 +68497,63 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
68241
68497
|
}
|
|
68242
68498
|
}
|
|
68243
68499
|
|
|
68244
|
-
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
|
+
}
|
|
68245
68557
|
|
|
68246
68558
|
const stableSelector = helpers.buildSelector ? helpers.buildSelector(el) : 'html';
|
|
68247
68559
|
const html = helpers.getOuterHtmlSnippet ? helpers.getOuterHtmlSnippet(el) : (el.outerHTML || '');
|
|
@@ -68271,11 +68583,10 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
68271
68583
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
68272
68584
|
}), applicability: null },
|
|
68273
68585
|
"slider-name-present": { run: (function runInPage(ctx) {
|
|
68274
|
-
const { document,
|
|
68586
|
+
const { document, helpers, rule } = ctx;
|
|
68275
68587
|
const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
|
|
68276
68588
|
? helpers.getEligibilityInfo
|
|
68277
68589
|
: null;
|
|
68278
|
-
const safeRoot = root || document;
|
|
68279
68590
|
|
|
68280
68591
|
|
|
68281
68592
|
function normalizeWs(s) {
|
|
@@ -68407,7 +68718,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
68407
68718
|
let applicableCount = 0;
|
|
68408
68719
|
|
|
68409
68720
|
const selector = 'input[type="range"], [role="slider"]';
|
|
68410
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
68721
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
68411
68722
|
|
|
68412
68723
|
|
|
68413
68724
|
// Precompute label[for] map for native range inputs.
|
|
@@ -68521,11 +68832,10 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
68521
68832
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
68522
68833
|
}), applicability: null },
|
|
68523
68834
|
"spinbutton-name-present": { run: (function runInPage(ctx) {
|
|
68524
|
-
const { document,
|
|
68835
|
+
const { document, helpers, rule } = ctx;
|
|
68525
68836
|
const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
|
|
68526
68837
|
? helpers.getEligibilityInfo
|
|
68527
68838
|
: null;
|
|
68528
|
-
const safeRoot = root || document;
|
|
68529
68839
|
|
|
68530
68840
|
|
|
68531
68841
|
function normalizeWs(s) {
|
|
@@ -68630,7 +68940,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
68630
68940
|
let applicableCount = 0;
|
|
68631
68941
|
|
|
68632
68942
|
const selector = "[role=\"spinbutton\"]";
|
|
68633
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
68943
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
68634
68944
|
|
|
68635
68945
|
// Precompute label[for] map for spinbutton elements that are labelable
|
|
68636
68946
|
// native form controls (e.g. <input role="spinbutton">).
|
|
@@ -68731,11 +69041,10 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
68731
69041
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
68732
69042
|
}), applicability: null },
|
|
68733
69043
|
"summary-name-present": { run: (function runInPage(ctx) {
|
|
68734
|
-
const { document,
|
|
69044
|
+
const { document, helpers, rule } = ctx;
|
|
68735
69045
|
const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
|
|
68736
69046
|
? helpers.getEligibilityInfo
|
|
68737
69047
|
: null;
|
|
68738
|
-
const safeRoot = root || document;
|
|
68739
69048
|
|
|
68740
69049
|
|
|
68741
69050
|
function normalizeWs(s) {
|
|
@@ -68798,7 +69107,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
68798
69107
|
const occurrences = [];
|
|
68799
69108
|
let applicableCount = 0;
|
|
68800
69109
|
|
|
68801
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('summary'
|
|
69110
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('summary') : helpers.queryAll('summary');
|
|
68802
69111
|
|
|
68803
69112
|
function evaluate(el) {
|
|
68804
69113
|
const ariaLabel = getAttr(el, 'aria-label');
|
|
@@ -69437,11 +69746,10 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
69437
69746
|
return {ruleId: rule.ruleId, outcome: 'cantTell', severity: 'minor', occurrences};
|
|
69438
69747
|
}), applicability: null },
|
|
69439
69748
|
"tab-name-present": { run: (function runInPage(ctx) {
|
|
69440
|
-
const { document,
|
|
69749
|
+
const { document, helpers, rule } = ctx;
|
|
69441
69750
|
const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
|
|
69442
69751
|
? helpers.getEligibilityInfo
|
|
69443
69752
|
: null;
|
|
69444
|
-
const safeRoot = root || document;
|
|
69445
69753
|
|
|
69446
69754
|
|
|
69447
69755
|
function normalizeWs(s) {
|
|
@@ -69547,7 +69855,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
69547
69855
|
let applicableCount = 0;
|
|
69548
69856
|
|
|
69549
69857
|
const selector = '[role="tab"]';
|
|
69550
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
69858
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
69551
69859
|
|
|
69552
69860
|
|
|
69553
69861
|
function evaluate(el) {
|
|
@@ -69662,14 +69970,13 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
69662
69970
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
69663
69971
|
}), applicability: null },
|
|
69664
69972
|
"table-duplicate-name": { run: (function runInPage(ctx) {
|
|
69665
|
-
const { document,
|
|
69666
|
-
const safeRoot = root || document;
|
|
69973
|
+
const { document, helpers, rule } = ctx;
|
|
69667
69974
|
|
|
69668
69975
|
function normalizeWs(s) {
|
|
69669
69976
|
return String(s || '').replace(/\s+/g, ' ').trim();
|
|
69670
69977
|
}
|
|
69671
69978
|
|
|
69672
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('table[summary]'
|
|
69979
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('table[summary]') : helpers.queryAll('table[summary]');
|
|
69673
69980
|
|
|
69674
69981
|
const occurrences = [];
|
|
69675
69982
|
let applicableCount = 0;
|
|
@@ -69715,12 +70022,11 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
69715
70022
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
69716
70023
|
}), applicability: null },
|
|
69717
70024
|
"table-fake-caption": { run: (function runInPage(ctx) {
|
|
69718
|
-
const { document,
|
|
69719
|
-
const safeRoot = root || document;
|
|
70025
|
+
const { document, helpers, rule } = ctx;
|
|
69720
70026
|
|
|
69721
70027
|
function trim(v) { return (v == null ? '' : String(v)).trim(); }
|
|
69722
70028
|
|
|
69723
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('table'
|
|
70029
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('table') : helpers.queryAll('table');
|
|
69724
70030
|
|
|
69725
70031
|
const occurrences = [];
|
|
69726
70032
|
let applicableCount = 0;
|
|
@@ -69783,10 +70089,9 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
69783
70089
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
69784
70090
|
}), applicability: null },
|
|
69785
70091
|
"table-headers-attr-valid": { run: (function runInPage(ctx) {
|
|
69786
|
-
const { document,
|
|
69787
|
-
const safeRoot = root || document;
|
|
70092
|
+
const { document, helpers, rule } = ctx;
|
|
69788
70093
|
|
|
69789
|
-
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]');
|
|
69790
70095
|
|
|
69791
70096
|
const occurrences = [];
|
|
69792
70097
|
let applicableCount = 0;
|
|
@@ -69860,10 +70165,9 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
69860
70165
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
69861
70166
|
}), applicability: null },
|
|
69862
70167
|
"table-th-has-data-cells": { run: (function runInPage(ctx) {
|
|
69863
|
-
const { document,
|
|
69864
|
-
const safeRoot = root || document;
|
|
70168
|
+
const { document, helpers, rule } = ctx;
|
|
69865
70169
|
|
|
69866
|
-
const tables = helpers.queryAllSmart ? helpers.queryAllSmart('table'
|
|
70170
|
+
const tables = helpers.queryAllSmart ? helpers.queryAllSmart('table') : helpers.queryAll('table');
|
|
69867
70171
|
|
|
69868
70172
|
const occurrences = [];
|
|
69869
70173
|
let applicableCount = 0;
|
|
@@ -70422,14 +70726,13 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
70422
70726
|
return { ruleId: RULE_ID, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
70423
70727
|
}), applicability: null },
|
|
70424
70728
|
"td-has-header": { run: (function runInPage(ctx) {
|
|
70425
|
-
const { document,
|
|
70426
|
-
const safeRoot = root || document;
|
|
70729
|
+
const { document, helpers, rule } = ctx;
|
|
70427
70730
|
|
|
70428
70731
|
const MIN_SIZE = 4;
|
|
70429
70732
|
|
|
70430
70733
|
function trim(v) { return (v == null ? '' : String(v)).trim(); }
|
|
70431
70734
|
|
|
70432
|
-
const tables = helpers.queryAllSmart ? helpers.queryAllSmart('table'
|
|
70735
|
+
const tables = helpers.queryAllSmart ? helpers.queryAllSmart('table') : helpers.queryAll('table');
|
|
70433
70736
|
|
|
70434
70737
|
const occurrences = [];
|
|
70435
70738
|
let applicableCount = 0;
|
|
@@ -70515,11 +70818,10 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
70515
70818
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
70516
70819
|
}), applicability: null },
|
|
70517
70820
|
"textbox-name-present": { run: (function runInPage(ctx) {
|
|
70518
|
-
const { document,
|
|
70821
|
+
const { document, helpers, rule } = ctx;
|
|
70519
70822
|
const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
|
|
70520
70823
|
? helpers.getEligibilityInfo
|
|
70521
70824
|
: null;
|
|
70522
|
-
const safeRoot = root || document;
|
|
70523
70825
|
|
|
70524
70826
|
|
|
70525
70827
|
function normalizeWs(s) {
|
|
@@ -70624,7 +70926,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
70624
70926
|
let applicableCount = 0;
|
|
70625
70927
|
|
|
70626
70928
|
const selector = "[role=\"textbox\"]";
|
|
70627
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
70929
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
70628
70930
|
|
|
70629
70931
|
// Precompute label[for] map for textbox elements that are labelable
|
|
70630
70932
|
// native form controls (e.g. <input role="textbox">).
|
|
@@ -70725,11 +71027,10 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
70725
71027
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
70726
71028
|
}), applicability: null },
|
|
70727
71029
|
"tooltip-name-present": { run: (function runInPage(ctx) {
|
|
70728
|
-
const { document,
|
|
71030
|
+
const { document, helpers, rule } = ctx;
|
|
70729
71031
|
const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
|
|
70730
71032
|
? helpers.getEligibilityInfo
|
|
70731
71033
|
: null;
|
|
70732
|
-
const safeRoot = root || document;
|
|
70733
71034
|
|
|
70734
71035
|
|
|
70735
71036
|
function normalizeWs(s) {
|
|
@@ -70793,7 +71094,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
70793
71094
|
let applicableCount = 0;
|
|
70794
71095
|
|
|
70795
71096
|
const selector = '[role="tooltip"]';
|
|
70796
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
71097
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
70797
71098
|
|
|
70798
71099
|
|
|
70799
71100
|
function evaluate(el) {
|
|
@@ -70857,11 +71158,10 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
70857
71158
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
70858
71159
|
}), applicability: null },
|
|
70859
71160
|
"treeitem-name-present": { run: (function runInPage(ctx) {
|
|
70860
|
-
const { document,
|
|
71161
|
+
const { document, helpers, rule } = ctx;
|
|
70861
71162
|
const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
|
|
70862
71163
|
? helpers.getEligibilityInfo
|
|
70863
71164
|
: null;
|
|
70864
|
-
const safeRoot = root || document;
|
|
70865
71165
|
|
|
70866
71166
|
|
|
70867
71167
|
function normalizeWs(s) {
|
|
@@ -70925,7 +71225,7 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
70925
71225
|
let applicableCount = 0;
|
|
70926
71226
|
|
|
70927
71227
|
const selector = "[role=\"treeitem\"]";
|
|
70928
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector
|
|
71228
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
|
|
70929
71229
|
|
|
70930
71230
|
|
|
70931
71231
|
function hasName(el) {
|
|
@@ -70988,12 +71288,11 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
70988
71288
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
70989
71289
|
}), applicability: null },
|
|
70990
71290
|
"valid-lang": { run: (function runInPage(ctx) {
|
|
70991
|
-
const { document,
|
|
70992
|
-
const safeRoot = root || document;
|
|
71291
|
+
const { document, helpers, rule } = ctx;
|
|
70993
71292
|
|
|
70994
71293
|
const BCP47_RE = /^[a-zA-Z]{2,3}(-[a-zA-Z0-9]{2,8})*$/;
|
|
70995
71294
|
|
|
70996
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[lang]'
|
|
71295
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[lang]') : helpers.queryAll('[lang]');
|
|
70997
71296
|
|
|
70998
71297
|
const occurrences = [];
|
|
70999
71298
|
let applicableCount = 0;
|
|
@@ -71038,10 +71337,9 @@ if (scan && scan.elements && Array.isArray(scan.elements)) {
|
|
|
71038
71337
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
71039
71338
|
}), applicability: null },
|
|
71040
71339
|
"video-caption": { run: (function runInPage(ctx) {
|
|
71041
|
-
const { document,
|
|
71042
|
-
const safeRoot = root || document;
|
|
71340
|
+
const { document, helpers, rule } = ctx;
|
|
71043
71341
|
|
|
71044
|
-
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('video'
|
|
71342
|
+
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('video') : helpers.queryAll('video');
|
|
71045
71343
|
|
|
71046
71344
|
const occurrences = [];
|
|
71047
71345
|
let applicableCount = 0;
|
|
@@ -71763,10 +72061,12 @@ const I18N = {
|
|
|
71763
72061
|
"region_description": "Checks that direct children of <body> with visible text content are contained within a landmark region.",
|
|
71764
72062
|
"region_summary_cantTell": "This content is not contained within a landmark region.",
|
|
71765
72063
|
"region_hint_cantTell": "Move this content inside a landmark region (main, nav, aside, a labeled section, etc.).",
|
|
71766
|
-
"skipLink_title": "Skip link must have a resolvable target",
|
|
71767
|
-
"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.",
|
|
71768
72066
|
"skipLink_summary_cantTell": "This skip link's target does not exist.",
|
|
71769
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.",
|
|
71770
72070
|
"autocompleteValid_title": "autocomplete attribute must be a valid autofill value",
|
|
71771
72071
|
"autocompleteValid_description": "Checks that a non-empty autocomplete attribute is \"on\"/\"off\" or a well-formed autofill detail token list.",
|
|
71772
72072
|
"autocompleteValid_summary_fail": "This autocomplete attribute value is not a valid autofill value.",
|
|
@@ -72365,10 +72665,12 @@ const I18N = {
|
|
|
72365
72665
|
"region_description": "Vérifie que les enfants directs de <body> ayant un contenu textuel visible sont contenus dans un point de repère.",
|
|
72366
72666
|
"region_summary_cantTell": "Ce contenu n’est contenu dans aucun point de repère.",
|
|
72367
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.).",
|
|
72368
|
-
"skipLink_title": "Un lien d’évitement doit avoir une cible qui se résout",
|
|
72369
|
-
"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.",
|
|
72370
72670
|
"skipLink_summary_cantTell": "La cible de ce lien d’évitement n’existe pas.",
|
|
72371
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.",
|
|
72372
72674
|
"autocompleteValid_title": "L’attribut autocomplete doit être une valeur d’auto-remplissage valide",
|
|
72373
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.",
|
|
72374
72676
|
"autocompleteValid_summary_fail": "Cette valeur d’attribut autocomplete n’est pas une valeur d’auto-remplissage valide.",
|
|
@@ -76151,7 +76453,8 @@ const createDomHelpers = (function createDomHelpers(opts) {
|
|
|
76151
76453
|
'templateContent',
|
|
76152
76454
|
'nonRenderedElement',
|
|
76153
76455
|
'inputHidden',
|
|
76154
|
-
'visibilityHidden'
|
|
76456
|
+
'visibilityHidden',
|
|
76457
|
+
'contentVisibilityHidden'
|
|
76155
76458
|
]);
|
|
76156
76459
|
|
|
76157
76460
|
function queryAllSmart(sel) {
|
|
@@ -76168,6 +76471,23 @@ const createDomHelpers = (function createDomHelpers(opts) {
|
|
|
76168
76471
|
for (const r of reasons) {
|
|
76169
76472
|
if (HARD_HIDDEN_REASONS.has(r)) return false;
|
|
76170
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
|
+
}
|
|
76171
76491
|
return true;
|
|
76172
76492
|
} catch {
|
|
76173
76493
|
return true;
|
|
@@ -78744,6 +79064,36 @@ const createDomHelpers = (function createDomHelpers(opts) {
|
|
|
78744
79064
|
let node = el;
|
|
78745
79065
|
let safety = 0;
|
|
78746
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
|
+
|
|
78747
79097
|
while (node && node.nodeType === 1 && safety++ < 20) {
|
|
78748
79098
|
let anchor = null;
|
|
78749
79099
|
|
|
@@ -78783,7 +79133,7 @@ const createDomHelpers = (function createDomHelpers(opts) {
|
|
|
78783
79133
|
parts.unshift(nthOfType(node));
|
|
78784
79134
|
}
|
|
78785
79135
|
|
|
78786
|
-
if (!node.parentElement || roots.includes(node)) break;
|
|
79136
|
+
if (!node.parentElement || (stopAtMatchedRoot && roots.includes(node))) break;
|
|
78787
79137
|
node = node.parentElement;
|
|
78788
79138
|
}
|
|
78789
79139
|
|
|
@@ -78802,7 +79152,12 @@ const createDomHelpers = (function createDomHelpers(opts) {
|
|
|
78802
79152
|
// position relative to its own parent via `>` (child, not
|
|
78803
79153
|
// descendant) combinators, so a correctly-matching chain can
|
|
78804
79154
|
// only resolve to one element short of a malformed document
|
|
78805
|
-
// (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
|
|
78806
79161
|
// document-wide :nth-of-type scan was measured to cost O(total
|
|
78807
79162
|
// same-tag siblings) per call — pathological on pages with many
|
|
78808
79163
|
// flat, unidentified siblings (e.g. hundreds of unlabeled
|
|
@@ -79382,6 +79737,21 @@ const runCore = (function runCore(pageUrl, contextSelector, engineOptions, runOn
|
|
|
79382
79737
|
? engineOptionsResolved.timestamp.trim()
|
|
79383
79738
|
: null;
|
|
79384
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
|
+
|
|
79385
79755
|
const sharedHelpers = createDomHelpers({
|
|
79386
79756
|
document,
|
|
79387
79757
|
window,
|