@a11y-pulse/tab-orchestrator 0.6.1 → 0.7.0
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/dist/browser-scripts.d.ts +33 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +129 -7
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +18 -2
- package/package.json +3 -3
|
@@ -26,6 +26,32 @@ export type BaselinePayload = {
|
|
|
26
26
|
* return a snapshot of their styles to use as a baseline.
|
|
27
27
|
*/
|
|
28
28
|
export declare function baselineScript(props: string[], markerAttr: string, limit: number): BaselinePayload;
|
|
29
|
+
/** Every element that can be a sequential focus stop, before tabindex, state and visibility. */
|
|
30
|
+
export declare const TABBABLE_SELECTOR: string;
|
|
31
|
+
/**
|
|
32
|
+
* Keep the most recent Tab key event. The listener sits on the window in the capture phase, so the
|
|
33
|
+
* page cannot stop it seeing the event, and `defaultPrevented` is read once dispatch has finished.
|
|
34
|
+
*/
|
|
35
|
+
export declare function installTabObserverScript(): void;
|
|
36
|
+
/** Whether the page cancelled the most recent Tab key press, as seen by `installTabObserverScript`. */
|
|
37
|
+
export declare function tabPreventedScript(): boolean;
|
|
38
|
+
export declare function removeTabObserverScript(): void;
|
|
39
|
+
export type HeldFocus = {
|
|
40
|
+
/** The page cancelled the Tab press that left focus where it was. */
|
|
41
|
+
prevented: boolean;
|
|
42
|
+
/** Something tabbable follows the focused element in document order, so the tab order goes on. */
|
|
43
|
+
followed: boolean;
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Why a Tab press left focus on the same element: the page cancelled it, or there is nothing
|
|
47
|
+
* tabbable after the element to move to.
|
|
48
|
+
*/
|
|
49
|
+
export declare function heldFocusScript(tabbableSelector: string): HeldFocus;
|
|
50
|
+
/**
|
|
51
|
+
* Give the focused element (descending open shadow roots) the marker `index` unless it already has
|
|
52
|
+
* one, and return its marker.
|
|
53
|
+
*/
|
|
54
|
+
export declare function markActiveElementScript(markerAttr: string, index: number): number;
|
|
29
55
|
export type ActiveElementBase = {
|
|
30
56
|
index: number | null;
|
|
31
57
|
isBody: boolean;
|
|
@@ -38,6 +64,13 @@ export type ActiveElementBase = {
|
|
|
38
64
|
html: string;
|
|
39
65
|
styles: StyleSnapshot;
|
|
40
66
|
rect: Rect;
|
|
67
|
+
/**
|
|
68
|
+
* The element sits on a fixed or sticky ancestor (or is one), so its page position depends on
|
|
69
|
+
* scrolling rather than on where it is in the page.
|
|
70
|
+
*/
|
|
71
|
+
pinned?: boolean;
|
|
72
|
+
/** The element is inside an open dialog, where focus management may legitimately leave the page order. */
|
|
73
|
+
inDialog?: boolean;
|
|
41
74
|
};
|
|
42
75
|
/**
|
|
43
76
|
* Read the currently focused element (descending open shadow roots), returning its marker index,
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
export type { AuditSelfDisconnect, BaseAuditOptions, BaseAuditSummary, } from "./audit-scaffold";
|
|
2
2
|
export { createAuditSelfDisconnect, DEFAULT_ELEMENT_LIMIT, DEFAULT_FAILED_ELEMENT_LIMIT, DEFAULT_SCREENSHOT_SETTLE_DELAY, DEFAULT_TIMEOUT, } from "./audit-scaffold";
|
|
3
3
|
export type { ActiveElementBase, BaselinePayload } from "./browser-scripts";
|
|
4
|
-
export { activeElementHandleScript, baselineScript, blurScript, clearMarkersScript, elementRectScript, elementStylesScript, focusScript, hasFocusScript, isCenterObscuredScript, needsCentringScript, pageDimensionsScript, probeActiveElementScript, scrollToCenterScript, } from "./browser-scripts";
|
|
4
|
+
export { activeElementHandleScript, baselineScript, blurScript, clearMarkersScript, elementRectScript, elementStylesScript, focusScript, hasFocusScript, heldFocusScript, installTabObserverScript, isCenterObscuredScript, markActiveElementScript, needsCentringScript, pageDimensionsScript, probeActiveElementScript, removeTabObserverScript, scrollToCenterScript, TABBABLE_SELECTOR, tabPreventedScript, } from "./browser-scripts";
|
|
5
5
|
export { FOCUS_STYLE_PROPERTIES } from "./focus-style";
|
|
6
6
|
export type { TabSessionOptions } from "./orchestrator";
|
|
7
7
|
export { createTabOrchestrator } from "./orchestrator";
|
|
8
|
-
export type { ActiveElementInfo, Capability, ContextChangeDrain, ContextChangeSignals, ObscuredMeasurement, SessionEndReason, StyleSnapshot, TabConsumer, TabSessionHandle, TabStopSnapshot, UnfocusedPair, } from "./types";
|
|
8
|
+
export type { ActiveElementInfo, Capability, ContextChangeDrain, ContextChangeSignals, ObscuredMeasurement, SessionEndDetail, SessionEndReason, StyleSnapshot, TabConsumer, TabSessionHandle, TabStopSnapshot, UnfocusedPair, } from "./types";
|
package/dist/index.js
CHANGED
|
@@ -137,6 +137,81 @@ function baselineScript(props, markerAttr, limit) {
|
|
|
137
137
|
}
|
|
138
138
|
return { styles, entries };
|
|
139
139
|
}
|
|
140
|
+
var TABBABLE_SELECTOR = [
|
|
141
|
+
"a[href]",
|
|
142
|
+
"area[href]",
|
|
143
|
+
"button",
|
|
144
|
+
"input:not([type='hidden'])",
|
|
145
|
+
"select",
|
|
146
|
+
"textarea",
|
|
147
|
+
"iframe",
|
|
148
|
+
"summary",
|
|
149
|
+
"audio[controls]",
|
|
150
|
+
"video[controls]",
|
|
151
|
+
"[tabindex]",
|
|
152
|
+
"[contenteditable]:not([contenteditable='false'])"
|
|
153
|
+
].join(", ");
|
|
154
|
+
function installTabObserverScript() {
|
|
155
|
+
const win = window;
|
|
156
|
+
if (win.__a11yPulseTabListener) {
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
win.__a11yPulseTabListener = (event) => {
|
|
160
|
+
if (event.key === "Tab") {
|
|
161
|
+
win.__a11yPulseLastTab = event;
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
window.addEventListener("keydown", win.__a11yPulseTabListener, true);
|
|
165
|
+
}
|
|
166
|
+
function tabPreventedScript() {
|
|
167
|
+
return window.__a11yPulseLastTab?.defaultPrevented === true;
|
|
168
|
+
}
|
|
169
|
+
function removeTabObserverScript() {
|
|
170
|
+
const win = window;
|
|
171
|
+
if (win.__a11yPulseTabListener) {
|
|
172
|
+
window.removeEventListener("keydown", win.__a11yPulseTabListener, true);
|
|
173
|
+
}
|
|
174
|
+
delete win.__a11yPulseTabListener;
|
|
175
|
+
delete win.__a11yPulseLastTab;
|
|
176
|
+
}
|
|
177
|
+
function heldFocusScript(tabbableSelector) {
|
|
178
|
+
let el = document.activeElement;
|
|
179
|
+
while (el?.shadowRoot?.activeElement) {
|
|
180
|
+
el = el.shadowRoot.activeElement;
|
|
181
|
+
}
|
|
182
|
+
let anchor = el;
|
|
183
|
+
while (anchor && anchor.getRootNode() instanceof ShadowRoot) {
|
|
184
|
+
anchor = anchor.getRootNode().host;
|
|
185
|
+
}
|
|
186
|
+
const modal = document.querySelector("dialog:modal");
|
|
187
|
+
const isTabbable = (candidate) => {
|
|
188
|
+
if (candidate.tabIndex < 0 || candidate.matches(":disabled") || candidate.closest("[inert]") || modal && !modal.contains(candidate)) {
|
|
189
|
+
return false;
|
|
190
|
+
}
|
|
191
|
+
const visibility = candidate;
|
|
192
|
+
if (typeof visibility.checkVisibility === "function") {
|
|
193
|
+
return visibility.checkVisibility({ checkVisibilityCSS: true });
|
|
194
|
+
}
|
|
195
|
+
return candidate.getClientRects().length > 0;
|
|
196
|
+
};
|
|
197
|
+
const prevented = window.__a11yPulseLastTab?.defaultPrevented === true;
|
|
198
|
+
const followed = anchor !== null && Array.from(document.querySelectorAll(tabbableSelector)).some(
|
|
199
|
+
(candidate) => candidate !== anchor && !anchor.contains(candidate) && (anchor.compareDocumentPosition(candidate) & Node.DOCUMENT_POSITION_FOLLOWING) !== 0 && isTabbable(candidate)
|
|
200
|
+
);
|
|
201
|
+
return { prevented, followed };
|
|
202
|
+
}
|
|
203
|
+
function markActiveElementScript(markerAttr, index) {
|
|
204
|
+
let el = document.activeElement;
|
|
205
|
+
while (el?.shadowRoot?.activeElement) {
|
|
206
|
+
el = el.shadowRoot.activeElement;
|
|
207
|
+
}
|
|
208
|
+
const existing = el?.getAttribute(markerAttr);
|
|
209
|
+
if (existing !== null && existing !== void 0) {
|
|
210
|
+
return Number(existing);
|
|
211
|
+
}
|
|
212
|
+
el?.setAttribute(markerAttr, String(index));
|
|
213
|
+
return index;
|
|
214
|
+
}
|
|
140
215
|
function probeActiveElementScript(props, markerAttr) {
|
|
141
216
|
let el = document.activeElement;
|
|
142
217
|
while (el?.shadowRoot?.activeElement) {
|
|
@@ -176,6 +251,12 @@ function probeActiveElementScript(props, markerAttr) {
|
|
|
176
251
|
};
|
|
177
252
|
const idxAttr = el.getAttribute(markerAttr);
|
|
178
253
|
const rect = el.getBoundingClientRect();
|
|
254
|
+
let pinned = false;
|
|
255
|
+
for (let node = el; node && !pinned; ) {
|
|
256
|
+
const { position } = getComputedStyle(node);
|
|
257
|
+
pinned = position === "fixed" || position === "sticky";
|
|
258
|
+
node = node.parentElement ?? node.getRootNode().host ?? null;
|
|
259
|
+
}
|
|
179
260
|
const openingTag = el.cloneNode(false);
|
|
180
261
|
openingTag.removeAttribute(markerAttr);
|
|
181
262
|
return {
|
|
@@ -193,7 +274,9 @@ function probeActiveElementScript(props, markerAttr) {
|
|
|
193
274
|
y: rect.top + window.scrollY,
|
|
194
275
|
width: rect.width,
|
|
195
276
|
height: rect.height
|
|
196
|
-
}
|
|
277
|
+
},
|
|
278
|
+
pinned,
|
|
279
|
+
inDialog: el.closest("dialog[open], [role='dialog'], [role='alertdialog']") !== null
|
|
197
280
|
};
|
|
198
281
|
}
|
|
199
282
|
function activeElementHandleScript() {
|
|
@@ -766,6 +849,8 @@ var DEFAULT_MARKER_LIMIT = 1024;
|
|
|
766
849
|
var MARKER_ATTR = "data-a11y-focus-idx";
|
|
767
850
|
var OBSCURER_ATTR = "data-a11y-obscurer";
|
|
768
851
|
var OBSCURING_RECHECK_DELAY_MS = 250;
|
|
852
|
+
var MAX_INTERNAL_STOPS = 10;
|
|
853
|
+
var MAX_IFRAME_STOPS = 256;
|
|
769
854
|
function createTabOrchestrator(adaptor, options = {}) {
|
|
770
855
|
const consumers = [];
|
|
771
856
|
let started = false;
|
|
@@ -845,9 +930,9 @@ function createTabOrchestrator(adaptor, options = {}) {
|
|
|
845
930
|
return screenshotByStop.current;
|
|
846
931
|
}
|
|
847
932
|
});
|
|
848
|
-
const end = (reason) => {
|
|
933
|
+
const end = (reason, revisitedIndex = null) => {
|
|
849
934
|
for (const consumer of attached) {
|
|
850
|
-
consumer.onSessionEnd?.(reason);
|
|
935
|
+
consumer.onSessionEnd?.(reason, { revisitedIndex });
|
|
851
936
|
}
|
|
852
937
|
attached.clear();
|
|
853
938
|
};
|
|
@@ -892,6 +977,7 @@ function createTabOrchestrator(adaptor, options = {}) {
|
|
|
892
977
|
MARKER_ATTR,
|
|
893
978
|
markerLimit
|
|
894
979
|
);
|
|
980
|
+
await adaptor.evaluate(installTabObserverScript);
|
|
895
981
|
const interned = /* @__PURE__ */ new Map();
|
|
896
982
|
for (const entry of payload.entries) {
|
|
897
983
|
const styles = payload.styles[entry.styleIndex];
|
|
@@ -908,6 +994,10 @@ function createTabOrchestrator(adaptor, options = {}) {
|
|
|
908
994
|
}
|
|
909
995
|
const visited = /* @__PURE__ */ new Set();
|
|
910
996
|
let tabIndex = 0;
|
|
997
|
+
let nextMarker = markerLimit;
|
|
998
|
+
let previousIndex = null;
|
|
999
|
+
let internalStops = 0;
|
|
1000
|
+
let iframeStops = 0;
|
|
911
1001
|
while (attached.size > 0) {
|
|
912
1002
|
pairByStop.current = null;
|
|
913
1003
|
screenshotByStop.current = null;
|
|
@@ -1011,15 +1101,37 @@ function createTabOrchestrator(adaptor, options = {}) {
|
|
|
1011
1101
|
break;
|
|
1012
1102
|
}
|
|
1013
1103
|
if (base.isIframe) {
|
|
1104
|
+
previousIndex = null;
|
|
1105
|
+
if (++iframeStops > MAX_IFRAME_STOPS) {
|
|
1106
|
+
end("stuck");
|
|
1107
|
+
break;
|
|
1108
|
+
}
|
|
1014
1109
|
continue;
|
|
1015
1110
|
}
|
|
1016
|
-
|
|
1017
|
-
|
|
1111
|
+
iframeStops = 0;
|
|
1112
|
+
if (base.index === null) {
|
|
1113
|
+
base.index = await adaptor.evaluate(markActiveElementScript, MARKER_ATTR, nextMarker);
|
|
1114
|
+
nextMarker = Math.max(nextMarker, base.index + 1);
|
|
1115
|
+
}
|
|
1116
|
+
if (base.index === previousIndex) {
|
|
1117
|
+
const held = await adaptor.evaluate(heldFocusScript, TABBABLE_SELECTOR);
|
|
1118
|
+
if (!held.prevented && !held.followed) {
|
|
1018
1119
|
end("completed");
|
|
1019
1120
|
break;
|
|
1020
1121
|
}
|
|
1021
|
-
|
|
1122
|
+
if (held.prevented || ++internalStops > MAX_INTERNAL_STOPS) {
|
|
1123
|
+
end("completed", base.index);
|
|
1124
|
+
break;
|
|
1125
|
+
}
|
|
1126
|
+
continue;
|
|
1022
1127
|
}
|
|
1128
|
+
previousIndex = base.index;
|
|
1129
|
+
internalStops = 0;
|
|
1130
|
+
if (visited.has(base.index)) {
|
|
1131
|
+
end("completed", base.index);
|
|
1132
|
+
break;
|
|
1133
|
+
}
|
|
1134
|
+
visited.add(base.index);
|
|
1023
1135
|
const ref = await adaptor.evaluateHandle(activeElementHandleScript);
|
|
1024
1136
|
activeHandle = ref;
|
|
1025
1137
|
try {
|
|
@@ -1128,6 +1240,10 @@ function createTabOrchestrator(adaptor, options = {}) {
|
|
|
1128
1240
|
await adaptor.evaluate(clearMarkersScript, MARKER_ATTR);
|
|
1129
1241
|
} catch {
|
|
1130
1242
|
}
|
|
1243
|
+
try {
|
|
1244
|
+
await adaptor.evaluate(removeTabObserverScript);
|
|
1245
|
+
} catch {
|
|
1246
|
+
}
|
|
1131
1247
|
}
|
|
1132
1248
|
if (failure !== void 0) {
|
|
1133
1249
|
end("failed");
|
|
@@ -1142,6 +1258,7 @@ export {
|
|
|
1142
1258
|
DEFAULT_SCREENSHOT_SETTLE_DELAY,
|
|
1143
1259
|
DEFAULT_TIMEOUT,
|
|
1144
1260
|
FOCUS_STYLE_PROPERTIES,
|
|
1261
|
+
TABBABLE_SELECTOR,
|
|
1145
1262
|
activeElementHandleScript,
|
|
1146
1263
|
baselineScript,
|
|
1147
1264
|
blurScript,
|
|
@@ -1152,10 +1269,15 @@ export {
|
|
|
1152
1269
|
elementStylesScript,
|
|
1153
1270
|
focusScript,
|
|
1154
1271
|
hasFocusScript,
|
|
1272
|
+
heldFocusScript,
|
|
1273
|
+
installTabObserverScript,
|
|
1155
1274
|
isCenterObscuredScript,
|
|
1275
|
+
markActiveElementScript,
|
|
1156
1276
|
needsCentringScript,
|
|
1157
1277
|
pageDimensionsScript,
|
|
1158
1278
|
probeActiveElementScript,
|
|
1159
|
-
|
|
1279
|
+
removeTabObserverScript,
|
|
1280
|
+
scrollToCenterScript,
|
|
1281
|
+
tabPreventedScript
|
|
1160
1282
|
};
|
|
1161
1283
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/audit-scaffold.ts","../src/browser-scripts.ts","../src/focus-style.ts","../src/orchestrator.ts","../src/capture-screenshot.ts","../src/unfocused-pair.ts"],"sourcesContent":["import type { SessionEndReason, TabSessionHandle } from \"./types\";\n\nexport const DEFAULT_ELEMENT_LIMIT = 1024;\nexport const DEFAULT_SCREENSHOT_SETTLE_DELAY = 33; // ~2 frames at 60fps\nexport const DEFAULT_FAILED_ELEMENT_LIMIT = 0; // 0 = never finish early\nexport const DEFAULT_TIMEOUT = 0; // 0 = no timeout\n\nexport type BaseAuditOptions = {\n\t/** Max focusable elements to tab through. */\n\telementLimit?: number;\n\t/** How long to wait (ms) after each Tab for the page to settle. */\n\tscreenshotSettleDelay?: number;\n\t/** Disconnect early after this many elements fail (0 = never). */\n\tfailedElementLimit?: number;\n\t/** Return whatever's gathered so far after this many ms (0 = no timeout). */\n\ttimeout?: number;\n};\n\nexport type BaseAuditSummary = {\n\tchecked: number;\n\tpassed: number;\n\tfailed: number;\n\t/** True if the element limit was hit before tabbing finished. */\n\treachedLimit: boolean;\n\t/** True if the audit stopped early after hitting `failedElementLimit`. */\n\treachedFailedElementLimit: boolean;\n\t/**\n\t * True if the audit returned early because `timeout` elapsed. The results are whatever had been\n\t * gathered when the deadline hit.\n\t */\n\ttimedOut: boolean;\n\t/**\n\t * Why the tab session ended, or `null` when this consumer disconnected itself (element limit,\n\t * failed-element limit, or timeout).\n\t */\n\tsessionEnd: SessionEndReason | null;\n};\n\nexport type AuditSelfDisconnect = {\n\t/** True once this consumer has disconnected itself from the session. */\n\treadonly disconnectedSelf: boolean;\n\t/** Arm a timeout that calls `onTimeout` then disconnects, if `timeout` is greater than 0. */\n\tarmTimeout(timeout: number, session: TabSessionHandle, onTimeout: () => void): void;\n\t/** Disconnect the session and record that this consumer initiated it. */\n\tdisconnect(session: TabSessionHandle): void;\n\t/** Clear any pending timeout without disconnecting. */\n\tclear(): void;\n};\n\n/**\n * Arms an optional self-disconnecting timeout and tracks whether this consumer initiated its own\n * disconnect, so `onSessionEnd` can tell that apart from an external end.\n */\nexport function createAuditSelfDisconnect(): AuditSelfDisconnect {\n\tlet timer: ReturnType<typeof setTimeout> | undefined;\n\tlet disconnectedSelf = false;\n\n\tconst clear = (): void => {\n\t\tif (timer !== undefined) {\n\t\t\tclearTimeout(timer);\n\t\t\ttimer = undefined;\n\t\t}\n\t};\n\n\tconst disconnect = (session: TabSessionHandle): void => {\n\t\tdisconnectedSelf = true;\n\t\tclear();\n\t\tsession.disconnect();\n\t};\n\n\treturn {\n\t\tget disconnectedSelf() {\n\t\t\treturn disconnectedSelf;\n\t\t},\n\t\tarmTimeout(timeout, session, onTimeout) {\n\t\t\tif (timeout > 0) {\n\t\t\t\ttimer = setTimeout(() => {\n\t\t\t\t\ttimer = undefined;\n\t\t\t\t\tonTimeout();\n\t\t\t\t\tdisconnect(session);\n\t\t\t\t}, timeout);\n\t\t\t}\n\t\t},\n\t\tdisconnect,\n\t\tclear,\n\t};\n}\n","/**\n * IMPORTANT: Functions in this file are serialized and injected into the audited page, so they must\n * not reference any symbols outside their own scope. This also means they cannot share code, so any\n * common logic (like descending into shadow roots) must be duplicated.\n */\n\nimport type { Rect } from \"@a11y-pulse/browser-adaptor\";\nimport type { StyleSnapshot } from \"./types\";\n\ntype BaselineElementEntry = {\n\tindex: number;\n\t/** Position of this element's snapshot in the payload's `styles` table. */\n\tstyleIndex: number;\n\t/** The element's page-relative rect in its unfocused (baseline) state. */\n\trect: Rect;\n};\n\n/**\n * The baseline as a table of unique style snapshots plus per-element entries referencing them.\n * Elements overwhelmingly share identical snapshots (same classes), so interning keeps the\n * serialised payload O(unique styles) instead of O(elements × style size).\n */\nexport type BaselinePayload = {\n\tstyles: StyleSnapshot[];\n\tentries: BaselineElementEntry[];\n};\n\n/**\n * Find up to `limit` focusable elements and mark them with `markerAttr` and a unique index. Then\n * return a snapshot of their styles to use as a baseline.\n */\nexport function baselineScript(\n\tprops: string[],\n\tmarkerAttr: string,\n\tlimit: number,\n): BaselinePayload {\n\t// Bound oversized computed values (data-URI background images can run to hundreds of KB) while\n\t// preserving inequality via the length and a fingerprint of the full value. Must be\n\t// byte-identical to the copy in probeActiveElementScript, or every oversized value would \"change\"\n\t// on focus and produce a false style-check pass.\n\tconst truncateValue = (value: string): string => {\n\t\tconst MAX_STYLE_VALUE_LENGTH = 512;\n\n\t\tif (value.length <= MAX_STYLE_VALUE_LENGTH) {\n\t\t\treturn value;\n\t\t}\n\n\t\tlet hash = 5381;\n\n\t\tfor (let i = 0; i < value.length; i++) {\n\t\t\thash = ((hash * 33) ^ value.charCodeAt(i)) >>> 0;\n\t\t}\n\n\t\treturn `${value.slice(0, MAX_STYLE_VALUE_LENGTH)}#len=${value.length}#h=${hash.toString(36)}`;\n\t};\n\n\tconst snapshot = (el: Element): StyleSnapshot => {\n\t\tconst read = (pseudo: string | undefined): Record<string, string> => {\n\t\t\tconst cs = getComputedStyle(el, pseudo);\n\t\t\tconst out: Record<string, string> = {};\n\n\t\t\tfor (const prop of props) {\n\t\t\t\tout[prop] = truncateValue(cs.getPropertyValue(prop));\n\t\t\t}\n\n\t\t\tif (pseudo) {\n\t\t\t\tout.content = truncateValue(cs.getPropertyValue(\"content\"));\n\t\t\t}\n\n\t\t\treturn out;\n\t\t};\n\n\t\treturn {\n\t\t\telement: read(undefined),\n\t\t\tbefore: read(\"::before\"),\n\t\t\tafter: read(\"::after\"),\n\t\t};\n\t};\n\n\tconst candidates: Element[] = [];\n\tconst focusableSelector = [\n\t\t\"a[href]\",\n\t\t\"button\",\n\t\t\"input\",\n\t\t\"select\",\n\t\t\"textarea\",\n\t\t\"[tabindex]\",\n\t\t'[contenteditable=\"\"]',\n\t\t'[contenteditable=\"true\"]',\n\t].join(\", \");\n\n\t// Collect focusable candidates in document order, descending into open shadow roots.\n\tconst collect = (root: Document | ShadowRoot): void => {\n\t\tfor (const el of Array.from(root.querySelectorAll(\"*\"))) {\n\t\t\tif (el.matches(focusableSelector)) {\n\t\t\t\tcandidates.push(el);\n\t\t\t}\n\n\t\t\tif (el.shadowRoot) {\n\t\t\t\tcollect(el.shadowRoot);\n\t\t\t}\n\t\t}\n\t};\n\n\tcollect(document);\n\n\tconst styles: StyleSnapshot[] = [];\n\tconst styleIndexByKey = new Map<string, number>();\n\tconst entries: BaselineElementEntry[] = [];\n\tlet idx = 0;\n\n\tfor (const el of candidates) {\n\t\tif (idx >= limit) {\n\t\t\tbreak;\n\t\t}\n\n\t\tif (el.hasAttribute(\"disabled\")) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tif ((el as HTMLElement).tabIndex < 0) {\n\t\t\t// Skip elements that are focusable but not in the tab order (negative tabindex). The audit\n\t\t\t// tabs through the page, so it never lands on them, and counting them would waste the\n\t\t\t// element-limit budget.\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Skip non-visible elements so they don't consume the marker budget (e.g. links inside a closed\n\t\t// mega-menu). checkVisibility sees hiding applied by ancestors, which per-element computed\n\t\t// styles don't (`display` doesn't inherit); fall back to the element's own styles where it's\n\t\t// unavailable.\n\t\tconst visibilityCheck = el as Element & {\n\t\t\tcheckVisibility?: (options?: { checkVisibilityCSS?: boolean }) => boolean;\n\t\t};\n\n\t\tif (typeof visibilityCheck.checkVisibility === \"function\") {\n\t\t\tif (!visibilityCheck.checkVisibility({ checkVisibilityCSS: true })) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t} else {\n\t\t\tconst cs = getComputedStyle(el);\n\n\t\t\tif (cs.display === \"none\" || cs.visibility === \"hidden\") {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\t// Mark before measuring: the probe reads styles and rects while the marker is present, so the\n\t\t// baseline must be captured in the same DOM state.\n\t\tel.setAttribute(markerAttr, String(idx));\n\n\t\tconst rect = el.getBoundingClientRect();\n\n\t\t// Intern the snapshot: the JSON key is deterministic because every snapshot is built with the\n\t\t// same property insertion order.\n\t\tconst snap = snapshot(el);\n\t\tconst key = JSON.stringify(snap);\n\t\tlet styleIndex = styleIndexByKey.get(key);\n\n\t\tif (styleIndex === undefined) {\n\t\t\tstyleIndex = styles.length;\n\t\t\tstyles.push(snap);\n\t\t\tstyleIndexByKey.set(key, styleIndex);\n\t\t}\n\n\t\tentries.push({\n\t\t\tindex: idx,\n\t\t\tstyleIndex,\n\t\t\trect: {\n\t\t\t\tx: rect.left + window.scrollX,\n\t\t\t\ty: rect.top + window.scrollY,\n\t\t\t\twidth: rect.width,\n\t\t\t\theight: rect.height,\n\t\t\t},\n\t\t});\n\t\tidx++;\n\t}\n\n\treturn { styles, entries };\n}\n\nexport type ActiveElementBase = {\n\tindex: number | null;\n\tisBody: boolean;\n\t/**\n\t * The focused element is an <iframe>. Tabbing into a frame passes focus into its embedded\n\t * document, but the parent's document.activeElement is the frame element itself, so the loop uses\n\t * this to skip the frame as a tab stop.\n\t */\n\tisIframe: boolean;\n\thtml: string;\n\tstyles: StyleSnapshot;\n\trect: Rect;\n};\n\n/**\n * Read the currently focused element (descending open shadow roots), returning its marker index,\n * opening-tag HTML (marker attribute stripped), focus-relevant styles, and page-relative rect.\n */\nexport function probeActiveElementScript(props: string[], markerAttr: string): ActiveElementBase {\n\tlet el = document.activeElement;\n\n\twhile (el?.shadowRoot?.activeElement) {\n\t\tel = el.shadowRoot.activeElement;\n\t}\n\n\tif (!el || el === document.body) {\n\t\treturn {\n\t\t\tindex: null,\n\t\t\tisBody: true,\n\t\t\tisIframe: false,\n\t\t\thtml: \"\",\n\t\t\tstyles: { element: {}, before: {}, after: {} },\n\t\t\trect: { x: 0, y: 0, width: 0, height: 0 },\n\t\t};\n\t}\n\n\t// Must be byte-identical to the copy in baselineScript: snapshots from the two scripts are\n\t// compared for equality, so they have to truncate oversized values the same way.\n\tconst truncateValue = (value: string): string => {\n\t\tconst MAX_STYLE_VALUE_LENGTH = 512;\n\n\t\tif (value.length <= MAX_STYLE_VALUE_LENGTH) {\n\t\t\treturn value;\n\t\t}\n\n\t\tlet hash = 5381;\n\n\t\tfor (let i = 0; i < value.length; i++) {\n\t\t\thash = ((hash * 33) ^ value.charCodeAt(i)) >>> 0;\n\t\t}\n\n\t\treturn `${value.slice(0, MAX_STYLE_VALUE_LENGTH)}#len=${value.length}#h=${hash.toString(36)}`;\n\t};\n\n\tconst read = (pseudo: string | undefined): Record<string, string> => {\n\t\tconst cs = getComputedStyle(el as Element, pseudo);\n\t\tconst out: Record<string, string> = {};\n\n\t\tfor (const prop of props) {\n\t\t\tout[prop] = truncateValue(cs.getPropertyValue(prop));\n\t\t}\n\n\t\tif (pseudo) {\n\t\t\tout.content = truncateValue(cs.getPropertyValue(\"content\"));\n\t\t}\n\n\t\treturn out;\n\t};\n\n\tconst idxAttr = el.getAttribute(markerAttr);\n\tconst rect = el.getBoundingClientRect();\n\n\t// Shallow clone so only the opening tag is serialised, not the whole subtree; drop the internal\n\t// marker attribute so it never leaks into the snippet.\n\tconst openingTag = el.cloneNode(false) as Element;\n\topeningTag.removeAttribute(markerAttr);\n\n\treturn {\n\t\tindex: idxAttr === null ? null : Number(idxAttr),\n\t\tisBody: false,\n\t\tisIframe: el.tagName === \"IFRAME\",\n\t\thtml: openingTag.outerHTML,\n\t\tstyles: {\n\t\t\telement: read(undefined),\n\t\t\tbefore: read(\"::before\"),\n\t\t\tafter: read(\"::after\"),\n\t\t},\n\t\trect: {\n\t\t\tx: rect.left + window.scrollX,\n\t\t\ty: rect.top + window.scrollY,\n\t\t\twidth: rect.width,\n\t\t\theight: rect.height,\n\t\t},\n\t};\n}\n\nexport function activeElementHandleScript(): Element | null {\n\tlet el = document.activeElement;\n\n\twhile (el?.shadowRoot?.activeElement) {\n\t\tel = el.shadowRoot.activeElement;\n\t}\n\n\treturn el;\n}\n\nexport function pageDimensionsScript(): { width: number; height: number } {\n\treturn {\n\t\twidth: document.documentElement.scrollWidth,\n\t\theight: document.documentElement.scrollHeight,\n\t};\n}\n\nexport function elementRectScript(el: Element | null): Rect {\n\tif (!el) {\n\t\treturn { x: 0, y: 0, width: 0, height: 0 };\n\t}\n\n\tconst rect = el.getBoundingClientRect();\n\n\treturn {\n\t\tx: rect.left + window.scrollX,\n\t\ty: rect.top + window.scrollY,\n\t\twidth: rect.width,\n\t\theight: rect.height,\n\t};\n}\n\n/**\n * Whether something unrelated (e.g. a fixed cookie banner) covers the element's centre point, so a\n * screenshot of the element's box would show the covering element instead.\n */\nexport function isCenterObscuredScript(el: Element | null): boolean {\n\tif (!el) {\n\t\treturn false;\n\t}\n\n\tconst rect = el.getBoundingClientRect();\n\tconst x = Math.min(Math.max(rect.left + rect.width / 2, 0), window.innerWidth - 1);\n\tconst y = Math.min(Math.max(rect.top + rect.height / 2, 0), window.innerHeight - 1);\n\n\tlet hit = document.elementFromPoint(x, y);\n\n\t// Descend into shadow roots so a hit on a shadow host resolves to the inner element.\n\twhile (hit?.shadowRoot) {\n\t\tconst inner = hit.shadowRoot.elementFromPoint(x, y);\n\n\t\tif (!inner || inner === hit) {\n\t\t\tbreak;\n\t\t}\n\n\t\thit = inner;\n\t}\n\n\tif (!hit) {\n\t\treturn false;\n\t}\n\n\treturn hit !== el && !el.contains(hit) && !hit.contains(el);\n}\n\n/**\n * Whether the element should be centred in the viewport before its screenshots are taken: either\n * something unrelated covers its centre (see isCenterObscuredScript, duplicated here because these\n * scripts cannot share code), or its screenshot clip (its box padded by `buffer`, cut off at the\n * document edges like bufferedClip) reaches past an edge of the viewport. A capture only shows what\n * is inside the viewport, so such a clip would lose an indicator drawn in the padding. A clip larger\n * than the viewport does not count, since scrolling cannot fit it anyway.\n */\nexport function needsCentringScript(el: Element | null, buffer: number): boolean {\n\tif (!el) {\n\t\treturn false;\n\t}\n\n\tconst rect = el.getBoundingClientRect();\n\tconst viewportWidth = document.documentElement.clientWidth;\n\tconst viewportHeight = document.documentElement.clientHeight;\n\n\tconst x = Math.min(Math.max(rect.left + rect.width / 2, 0), viewportWidth - 1);\n\tconst y = Math.min(Math.max(rect.top + rect.height / 2, 0), viewportHeight - 1);\n\n\tlet hit = document.elementFromPoint(x, y);\n\n\twhile (hit?.shadowRoot) {\n\t\tconst inner = hit.shadowRoot.elementFromPoint(x, y);\n\n\t\tif (!inner || inner === hit) {\n\t\t\tbreak;\n\t\t}\n\n\t\thit = inner;\n\t}\n\n\tif (hit && hit !== el && !el.contains(hit) && !hit.contains(el)) {\n\t\treturn true;\n\t}\n\n\tconst docLeft = -window.scrollX;\n\tconst docTop = -window.scrollY;\n\tconst left = Math.max(rect.left - buffer, docLeft);\n\tconst top = Math.max(rect.top - buffer, docTop);\n\tconst right = Math.min(rect.right + buffer, docLeft + document.documentElement.scrollWidth);\n\tconst bottom = Math.min(rect.bottom + buffer, docTop + document.documentElement.scrollHeight);\n\n\tif (right - left > viewportWidth || bottom - top > viewportHeight) {\n\t\treturn false;\n\t}\n\n\treturn left < 0 || top < 0 || right > viewportWidth || bottom > viewportHeight;\n}\n\n/**\n * Scroll the window so the element sits at the vertical centre of the viewport and is inside it\n * horizontally with `buffer` to spare, instantly so a screenshot taken immediately afterwards isn't\n * captured mid-animation. Scrolls the window directly rather than via scrollIntoView(): Chromium\n * moves the sequential focus navigation starting point to the element passed to scrollIntoView(),\n * so when focus cannot be handed back afterwards (a closed shadow host) the next Tab would re-enter\n * the same element instead of moving on.\n */\nexport function scrollToCenterScript(el: Element | null, buffer = 0): void {\n\tif (!el) {\n\t\treturn;\n\t}\n\n\tconst rect = el.getBoundingClientRect();\n\tconst viewportWidth = document.documentElement.clientWidth;\n\tconst top = rect.top + rect.height / 2 - document.documentElement.clientHeight / 2;\n\tlet left = 0;\n\n\tif (rect.left - buffer < 0) {\n\t\tleft = rect.left - buffer;\n\t} else if (rect.right + buffer > viewportWidth) {\n\t\tleft = Math.min(rect.right + buffer - viewportWidth, rect.left - buffer);\n\t}\n\n\twindow.scrollBy({ top, left, behavior: \"instant\" });\n}\n\nexport function blurScript(el: Element | null): void {\n\t(el as HTMLElement | null)?.blur();\n}\n\nexport function focusScript(el: Element | null): void {\n\t(el as HTMLElement | null)?.focus({ preventScroll: true });\n}\n\n/**\n * Read focus-relevant computed styles from a given element. Unlike `probeActiveElementScript`, this\n * does not use `document.activeElement`, so it still works after the node has been blurred.\n */\nexport function elementStylesScript(el: Element | null, props: string[]): StyleSnapshot {\n\tif (!el) {\n\t\treturn { element: {}, before: {}, after: {} };\n\t}\n\n\t// Must be byte-identical to the copies in baselineScript and probeActiveElementScript: snapshots\n\t// from the scripts are compared for equality, so they have to truncate oversized values the same\n\t// way.\n\tconst truncateValue = (value: string): string => {\n\t\tconst MAX_STYLE_VALUE_LENGTH = 512;\n\n\t\tif (value.length <= MAX_STYLE_VALUE_LENGTH) {\n\t\t\treturn value;\n\t\t}\n\n\t\tlet hash = 5381;\n\n\t\tfor (let i = 0; i < value.length; i++) {\n\t\t\thash = ((hash * 33) ^ value.charCodeAt(i)) >>> 0;\n\t\t}\n\n\t\treturn `${value.slice(0, MAX_STYLE_VALUE_LENGTH)}#len=${value.length}#h=${hash.toString(36)}`;\n\t};\n\n\tconst read = (pseudo: string | undefined): Record<string, string> => {\n\t\tconst cs = getComputedStyle(el, pseudo);\n\t\tconst out: Record<string, string> = {};\n\n\t\tfor (const prop of props) {\n\t\t\tout[prop] = truncateValue(cs.getPropertyValue(prop));\n\t\t}\n\n\t\tif (pseudo) {\n\t\t\tout.content = truncateValue(cs.getPropertyValue(\"content\"));\n\t\t}\n\n\t\treturn out;\n\t};\n\n\treturn {\n\t\telement: read(undefined),\n\t\tbefore: read(\"::before\"),\n\t\tafter: read(\"::after\"),\n\t};\n}\n\n/**\n * Remove the marker attribute from all elements, blur the active element, and scroll back to the\n * top. This is a best-effort attempt to reset state between audits: the tab loop scrolls later\n * elements into view as it walks the page (see scrollToCenterScript), and nothing else undoes that\n * once the session ends.\n */\nexport function clearMarkersScript(markerAttr: string): void {\n\tconst clear = (root: Document | ShadowRoot): void => {\n\t\tfor (const el of Array.from(root.querySelectorAll(`[${markerAttr}]`))) {\n\t\t\tel.removeAttribute(markerAttr);\n\t\t}\n\n\t\t// Descend into any shadow roots to clear markers within them.\n\t\tfor (const el of Array.from(root.querySelectorAll(\"*\"))) {\n\t\t\tif (el.shadowRoot) {\n\t\t\t\tclear(el.shadowRoot);\n\t\t\t}\n\t\t}\n\t};\n\n\tclear(document);\n\n\t(document.activeElement as HTMLElement | null)?.blur();\n\n\twindow.scrollTo(0, 0);\n}\n\nexport type MeasureObscuringResult = {\n\tcoveredFraction: number;\n\tfullyObscured: boolean;\n\toffscreen: boolean;\n\topacity: \"opaque\" | \"semi-transparent\" | \"unknown\";\n\tobscuredByHtml: string | null;\n\t/** True when a covering element was marked for selector resolution. */\n\thasObscurer: boolean;\n};\n\n/**\n * Hit-test whether author-created content entirely hides the focused element. Marks a single\n * covering element with `obscurerAttr` when containment confirms a full cover, so the host can\n * resolve its selector via getSelector.\n */\nexport function measureObscuringScript(\n\tel: Element | null,\n\tobscurerAttr: string,\n): MeasureObscuringResult {\n\tconst empty: MeasureObscuringResult = {\n\t\tcoveredFraction: 0,\n\t\tfullyObscured: false,\n\t\toffscreen: false,\n\t\topacity: \"opaque\",\n\t\tobscuredByHtml: null,\n\t\thasObscurer: false,\n\t};\n\n\tif (!el) {\n\t\treturn empty;\n\t}\n\n\tfor (const marked of Array.from(document.querySelectorAll(`[${obscurerAttr}]`))) {\n\t\tmarked.removeAttribute(obscurerAttr);\n\t}\n\n\tconst rect = el.getBoundingClientRect();\n\tconst left = Math.max(rect.left, 0);\n\tconst top = Math.max(rect.top, 0);\n\tconst right = Math.min(rect.right, window.innerWidth);\n\tconst bottom = Math.min(rect.bottom, window.innerHeight);\n\n\tif (right <= left || bottom <= top) {\n\t\treturn { ...empty, offscreen: true };\n\t}\n\n\tconst width = right - left;\n\tconst height = bottom - top;\n\tconst LARGE_ELEMENT_THRESHOLD_PX = 80;\n\n\tconst points: Array<{ x: number; y: number }> = [\n\t\t{ x: left, y: top },\n\t\t{ x: right - 0.5, y: top },\n\t\t{ x: left, y: bottom - 0.5 },\n\t\t{ x: right - 0.5, y: bottom - 0.5 },\n\t\t{ x: left + width / 2, y: top },\n\t\t{ x: left + width / 2, y: bottom - 0.5 },\n\t\t{ x: left, y: top + height / 2 },\n\t\t{ x: right - 0.5, y: top + height / 2 },\n\t\t{ x: left + width / 2, y: top + height / 2 },\n\t];\n\n\tif (width > LARGE_ELEMENT_THRESHOLD_PX || height > LARGE_ELEMENT_THRESHOLD_PX) {\n\t\tpoints.push(\n\t\t\t{ x: left + width * 0.25, y: top + height * 0.25 },\n\t\t\t{ x: left + width * 0.75, y: top + height * 0.25 },\n\t\t\t{ x: left + width * 0.25, y: top + height * 0.75 },\n\t\t\t{ x: left + width * 0.75, y: top + height * 0.75 },\n\t\t);\n\t}\n\n\tconst hitAt = (x: number, y: number): Element | null => {\n\t\tconst px = Math.min(Math.max(x, 0), window.innerWidth - 1);\n\t\tconst py = Math.min(Math.max(y, 0), window.innerHeight - 1);\n\t\tconst stack =\n\t\t\ttypeof document.elementsFromPoint === \"function\"\n\t\t\t\t? document.elementsFromPoint(px, py)\n\t\t\t\t: [document.elementFromPoint(px, py)].filter(Boolean);\n\n\t\tfor (let candidate of stack as Element[]) {\n\t\t\twhile (candidate?.shadowRoot) {\n\t\t\t\tconst inner = candidate.shadowRoot.elementFromPoint(px, py);\n\n\t\t\t\tif (!inner || inner === candidate) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcandidate = inner;\n\t\t\t}\n\n\t\t\tif (!candidate) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (candidate === el || el.contains(candidate) || candidate.contains(el)) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\treturn candidate;\n\t\t}\n\n\t\treturn null;\n\t};\n\n\tlet covered = 0;\n\tconst coverCounts = new Map<Element, number>();\n\n\tfor (const point of points) {\n\t\tconst cover = hitAt(point.x, point.y);\n\n\t\tif (cover) {\n\t\t\tcovered++;\n\t\t\tcoverCounts.set(cover, (coverCounts.get(cover) ?? 0) + 1);\n\t\t}\n\t}\n\n\tconst coveredFraction = points.length === 0 ? 0 : covered / points.length;\n\n\tif (coveredFraction < 1) {\n\t\treturn {\n\t\t\t...empty,\n\t\t\tcoveredFraction,\n\t\t\tfullyObscured: false,\n\t\t};\n\t}\n\n\tlet topCover: Element | null = null;\n\tlet topCount = 0;\n\n\tfor (const [candidate, count] of coverCounts) {\n\t\tif (count > topCount) {\n\t\t\ttopCover = candidate;\n\t\t\ttopCount = count;\n\t\t}\n\t}\n\n\tif (!topCover) {\n\t\treturn {\n\t\t\t...empty,\n\t\t\tcoveredFraction: 1,\n\t\t\tfullyObscured: false,\n\t\t\topacity: \"unknown\",\n\t\t};\n\t}\n\n\tconst coverRect = topCover.getBoundingClientRect();\n\tconst containsFully =\n\t\tcoverRect.left <= rect.left &&\n\t\tcoverRect.top <= rect.top &&\n\t\tcoverRect.right >= rect.right &&\n\t\tcoverRect.bottom >= rect.bottom;\n\n\tif (!containsFully) {\n\t\t// Joint multi-element cover: cannot confirm a single opaque container.\n\t\treturn {\n\t\t\tcoveredFraction: 1,\n\t\t\tfullyObscured: true,\n\t\t\toffscreen: false,\n\t\t\topacity: \"unknown\",\n\t\t\tobscuredByHtml: null,\n\t\t\thasObscurer: false,\n\t\t};\n\t}\n\n\tconst classifyOpacity = (node: Element): \"opaque\" | \"semi-transparent\" | \"unknown\" => {\n\t\tconst cs = getComputedStyle(node);\n\t\tconst opacity = Number.parseFloat(cs.opacity);\n\n\t\tif (!Number.isFinite(opacity)) {\n\t\t\treturn \"unknown\";\n\t\t}\n\n\t\tif (opacity < 1) {\n\t\t\treturn \"semi-transparent\";\n\t\t}\n\n\t\tconst bg = cs.backgroundColor;\n\t\tconst rgba = bg.match(\n\t\t\t/rgba?\\(\\s*([\\d.]+)\\s*,\\s*([\\d.]+)\\s*,\\s*([\\d.]+)(?:\\s*,\\s*([\\d.]+))?\\s*\\)/,\n\t\t);\n\n\t\tif (rgba) {\n\t\t\tconst alpha = rgba[4] === undefined ? 1 : Number.parseFloat(rgba[4]);\n\n\t\t\tif (alpha < 1) {\n\t\t\t\treturn alpha <= 0 ? \"semi-transparent\" : \"semi-transparent\";\n\t\t\t}\n\n\t\t\treturn \"opaque\";\n\t\t}\n\n\t\tif (cs.backgroundImage && cs.backgroundImage !== \"none\") {\n\t\t\treturn \"unknown\";\n\t\t}\n\n\t\treturn \"unknown\";\n\t};\n\n\tconst opacity = classifyOpacity(topCover);\n\tconst openingTag = topCover.cloneNode(false) as Element;\n\topeningTag.removeAttribute(obscurerAttr);\n\ttopCover.setAttribute(obscurerAttr, \"1\");\n\n\treturn {\n\t\tcoveredFraction: 1,\n\t\tfullyObscured: true,\n\t\toffscreen: false,\n\t\topacity,\n\t\tobscuredByHtml: openingTag.outerHTML,\n\t\thasObscurer: true,\n\t};\n}\n\n/** Return the element marked as the obscurer, if any. */\nexport function obscurerHandleScript(): Element | null {\n\treturn document.querySelector(\"[data-a11y-obscurer]\");\n}\n\nexport function clearObscurerScript(obscurerAttr: string): void {\n\tfor (const marked of Array.from(document.querySelectorAll(`[${obscurerAttr}]`))) {\n\t\tmarked.removeAttribute(obscurerAttr);\n\t}\n}\n\ntype ContextObserverState = {\n\tbaselineHref: string;\n\topenedWindow: boolean;\n\tsubmittedForm: boolean;\n\t/** Elements that received focusin since the last drain. */\n\tfocusIns: Element[];\n\tsoftUrlChange: boolean;\n\tinstalled: boolean;\n};\n\ndeclare global {\n\tinterface Window {\n\t\t__a11yContextObserver?: ContextObserverState;\n\t}\n}\n\n/**\n * Install once: wrap window.open (record + inert stub), capture-phase submit (record +\n * preventDefault), track focusin and soft URL changes.\n */\nexport function installContextObserverScript(): void {\n\tif (window.__a11yContextObserver?.installed) {\n\t\treturn;\n\t}\n\n\tconst state: ContextObserverState = {\n\t\tbaselineHref: location.href,\n\t\topenedWindow: false,\n\t\tsubmittedForm: false,\n\t\tfocusIns: [],\n\t\tsoftUrlChange: false,\n\t\tinstalled: true,\n\t};\n\n\twindow.__a11yContextObserver = state;\n\n\tconst originalOpen = window.open.bind(window);\n\n\twindow.open = (..._args: Parameters<typeof window.open>) => {\n\t\tstate.openedWindow = true;\n\n\t\treturn {\n\t\t\tclosed: true,\n\t\t\tclose() {},\n\t\t\tfocus() {},\n\t\t\tblur() {},\n\t\t\topener: null,\n\t\t\tlocation: { href: \"\" },\n\t\t} as unknown as Window;\n\t};\n\n\t// Keep a reference so tooling does not tree-shake the bind as unused.\n\tvoid originalOpen;\n\n\tdocument.addEventListener(\n\t\t\"submit\",\n\t\t(event) => {\n\t\t\tstate.submittedForm = true;\n\t\t\tevent.preventDefault();\n\t\t\tevent.stopPropagation();\n\t\t},\n\t\ttrue,\n\t);\n\n\tdocument.addEventListener(\n\t\t\"focus\",\n\t\t(event) => {\n\t\t\tconst target = event.target;\n\n\t\t\t// Prefer nodeType: instanceof can be brittle across some embeddings.\n\t\t\tif (target && (target as Node).nodeType === 1) {\n\t\t\t\tstate.focusIns.push(target as Element);\n\t\t\t}\n\t\t},\n\t\ttrue,\n\t);\n\n\tconst noteSoftNav = (): void => {\n\t\tif (location.href !== state.baselineHref) {\n\t\t\tstate.softUrlChange = true;\n\t\t}\n\t};\n\n\twindow.addEventListener(\"hashchange\", noteSoftNav);\n\twindow.addEventListener(\"popstate\", noteSoftNav);\n\n\tconst wrapHistory = (method: \"pushState\" | \"replaceState\"): void => {\n\t\tconst original = history[method].bind(history);\n\n\t\thistory[method] = ((...args: Parameters<History[\"pushState\"]>) => {\n\t\t\tconst result = original(...args);\n\t\t\tnoteSoftNav();\n\n\t\t\treturn result;\n\t\t}) as History[\"pushState\"];\n\t};\n\n\twrapHistory(\"pushState\");\n\twrapHistory(\"replaceState\");\n}\n\nexport type DrainContextObserverResult = {\n\topenedWindow: boolean;\n\tsubmittedForm: boolean;\n\tfocusRemoved: boolean;\n\tredirect: \"outside\" | \"same-subtree\" | null;\n\tsoftUrlChange: boolean;\n\tnavigation: boolean;\n\tattributedHtml: string | null;\n\t/** True when an attributed element was marked for selector resolution. */\n\thasAttributed: boolean;\n};\n\n/**\n * Drain observer signals for the current tab stop and reset per-stop flags. Classification of focus\n * removal / redirection uses the post-Tab settle activeElement and the focusin history from this\n * stop only.\n */\nexport function drainContextObserverScript(markerAttr: string): DrainContextObserverResult {\n\t// Must be a string literal inside this function: it is serialized into the page.\n\tconst attributedAttr = \"data-a11y-ctx-attr\";\n\n\tconst empty: DrainContextObserverResult = {\n\t\topenedWindow: false,\n\t\tsubmittedForm: false,\n\t\tfocusRemoved: false,\n\t\tredirect: null,\n\t\tsoftUrlChange: false,\n\t\tnavigation: false,\n\t\tattributedHtml: null,\n\t\thasAttributed: false,\n\t};\n\n\tconst state = window.__a11yContextObserver;\n\n\tif (!state) {\n\t\treturn empty;\n\t}\n\n\tfor (const marked of Array.from(document.querySelectorAll(`[${attributedAttr}]`))) {\n\t\tmarked.removeAttribute(attributedAttr);\n\t}\n\n\tconst openedWindow = state.openedWindow;\n\tconst submittedForm = state.submittedForm;\n\tconst softUrlChange = state.softUrlChange;\n\tconst focusIns = state.focusIns.slice();\n\n\tstate.openedWindow = false;\n\tstate.submittedForm = false;\n\tstate.softUrlChange = false;\n\tstate.focusIns = [];\n\n\tlet active = document.activeElement;\n\n\twhile (active?.shadowRoot?.activeElement) {\n\t\tactive = active.shadowRoot.activeElement;\n\t}\n\n\tconst intended =\n\t\tfocusIns.find((el) => el !== document.body && el !== document.documentElement) ?? null;\n\n\tconst openingHtml = (node: Element): string => {\n\t\tconst clone = node.cloneNode(false) as Element;\n\t\tclone.removeAttribute(markerAttr);\n\t\tclone.removeAttribute(attributedAttr);\n\n\t\treturn clone.outerHTML;\n\t};\n\n\tlet focusRemoved = false;\n\tlet redirect: \"outside\" | \"same-subtree\" | null = null;\n\tlet attributedHtml: string | null = null;\n\tlet hasAttributed = false;\n\n\tconst bodyOrNone = !active || active === document.body || active === document.documentElement;\n\n\tif (intended && bodyOrNone) {\n\t\tfocusRemoved = true;\n\t\tattributedHtml = openingHtml(intended);\n\t\tintended.setAttribute(attributedAttr, \"1\");\n\t\thasAttributed = true;\n\t} else if (intended && active && intended !== active) {\n\t\tconst composedContains = (ancestor: Element, node: Element): boolean => {\n\t\t\tlet current: Node | null = node;\n\n\t\t\twhile (current) {\n\t\t\t\tif (current === ancestor) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\n\t\t\t\tconst parent: Node | null = current.parentNode;\n\n\t\t\t\tif (parent) {\n\t\t\t\t\tcurrent = parent;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif (current instanceof ShadowRoot) {\n\t\t\t\t\tcurrent = current.host;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\treturn false;\n\t\t};\n\n\t\tconst same = composedContains(intended, active) || composedContains(active, intended);\n\n\t\tredirect = same ? \"same-subtree\" : \"outside\";\n\t\tattributedHtml = openingHtml(intended);\n\t\tintended.setAttribute(attributedAttr, \"1\");\n\t\thasAttributed = true;\n\t}\n\n\treturn {\n\t\topenedWindow,\n\t\tsubmittedForm,\n\t\tfocusRemoved,\n\t\tredirect,\n\t\tsoftUrlChange,\n\t\tnavigation: false,\n\t\tattributedHtml,\n\t\thasAttributed,\n\t};\n}\n\n/**\n * Return the element marked as attributed by drainContextObserverScript (the element that received\n * a focus event this stop and then lost it), if any.\n */\nexport function attributedHandleScript(): Element | null {\n\treturn document.querySelector(\"[data-a11y-ctx-attr]\");\n}\n\nexport function clearAttributedScript(): void {\n\tfor (const marked of Array.from(document.querySelectorAll(\"[data-a11y-ctx-attr]\"))) {\n\t\tmarked.removeAttribute(\"data-a11y-ctx-attr\");\n\t}\n}\n\n/**\n * Drop focusin history accumulated after drain (e.g. pixel-diff blur/refocus) so the next tab stop\n * does not mis-attribute F55 or redirects.\n */\nexport function clearContextFocusInsScript(): void {\n\tconst state = window.__a11yContextObserver;\n\n\tif (state) {\n\t\tstate.focusIns = [];\n\t\tstate.openedWindow = false;\n\t\tstate.submittedForm = false;\n\t\tstate.softUrlChange = false;\n\t}\n}\n\n/** Current location.href for navigation comparison in the host. */\nexport function locationHrefScript(): string {\n\treturn location.href;\n}\n\nexport function hasFocusScript(): boolean {\n\treturn document.hasFocus();\n}\n","/**\n * Computed-style properties that are likely to draw a visible focus indicator. Layout properties\n * like margin and padding are deliberately excluded.\n */\nexport const FOCUS_STYLE_PROPERTIES = [\n\t\"background-color\",\n\t\"background-image\",\n\t\"border-bottom-color\",\n\t\"border-bottom-style\",\n\t\"border-bottom-width\",\n\t\"border-left-color\",\n\t\"border-left-style\",\n\t\"border-left-width\",\n\t\"border-right-color\",\n\t\"border-right-style\",\n\t\"border-right-width\",\n\t\"border-top-color\",\n\t\"border-top-style\",\n\t\"border-top-width\",\n\t\"box-shadow\",\n\t\"color\",\n\t\"font-weight\",\n\t\"outline-color\",\n\t\"outline-style\",\n\t\"outline-width\",\n\t\"text-decoration\",\n] as const;\n","import type { BrowserAdaptor, ElementRef } from \"@a11y-pulse/browser-adaptor\";\nimport { getSelector, truncateHtml } from \"@a11y-pulse/browser-adaptor/dom\";\nimport {\n\ttype ActiveElementBase,\n\tactiveElementHandleScript,\n\tattributedHandleScript,\n\tbaselineScript,\n\tclearAttributedScript,\n\tclearContextFocusInsScript,\n\tclearMarkersScript,\n\tclearObscurerScript,\n\ttype DrainContextObserverResult,\n\tdrainContextObserverScript,\n\thasFocusScript,\n\tinstallContextObserverScript,\n\tlocationHrefScript,\n\tmeasureObscuringScript,\n\tobscurerHandleScript,\n\tprobeActiveElementScript,\n} from \"./browser-scripts\";\nimport { captureScreenshot } from \"./capture-screenshot\";\nimport { FOCUS_STYLE_PROPERTIES } from \"./focus-style\";\nimport type {\n\tActiveElementInfo,\n\tCapability,\n\tContextChangeSignals,\n\tSessionEndReason,\n\tStyleSnapshot,\n\tTabConsumer,\n\tTabSessionHandle,\n\tTabStopSnapshot,\n\tUnfocusedPair,\n} from \"./types\";\nimport { captureUnfocusedPair } from \"./unfocused-pair\";\n\n/**\n * Whether an `evaluate()` rejection looks like the page navigated out from\n * under it (execution context destroyed) rather than an unexpected failure.\n * A full navigation is the one context-change signal the in-page observer\n * can never self-report: it destroys the JS context before it can log\n * anything, so the host has to infer it from how `evaluate()` failed.\n */\nexport function isContextDestroyedError(error: unknown): boolean {\n\tconst message = error instanceof Error ? error.message : String(error);\n\n\t// Each adaptor's driver words this differently: \"Target closed\" is Puppeteer's, \"has been\n\t// closed\" is Playwright's.\n\treturn /Execution context was destroyed|Target closed|has been closed|frame was detached|navigat/i.test(\n\t\tmessage,\n\t);\n}\n\n/**\n * Outcome of the early, pre-isBody navigation check. `navigation: true`\n * means a full navigation was detected (via href diff or a destroyed-context\n * error) and the caller must end the session without proceeding any\n * further. Otherwise `raw` carries a drain payload already fetched while\n * resolving a soft URL change (so the later full drain can reuse it instead\n * of reading back state the first call already reset), or `null` when the\n * href was unchanged and nothing has been drained yet for this stop.\n */\ntype ContextNavigationOutcome =\n\t| { navigation: true }\n\t| { navigation: false; raw: DrainContextObserverResult | null };\n\nexport const DEFAULT_SCREENSHOT_SETTLE_DELAY = 33;\nexport const DEFAULT_SCREENSHOT_CLIP_BUFFER = 10;\nexport const DEFAULT_MARKER_LIMIT = 1024;\nexport const MARKER_ATTR = \"data-a11y-focus-idx\";\nexport const OBSCURER_ATTR = \"data-a11y-obscurer\";\nexport const OBSCURING_RECHECK_DELAY_MS = 250;\n\nexport type TabSessionOptions = {\n\tscreenshotSettleDelay?: number;\n\tscreenshotClipBuffer?: number;\n\tmarkerLimit?: number;\n\tbaselineElementLimit?: number;\n};\n\nexport type TabOrchestrator = {\n\tattach(consumer: TabConsumer): void;\n\trun(): Promise<void>;\n};\n\nexport function createTabOrchestrator(\n\tadaptor: BrowserAdaptor,\n\toptions: TabSessionOptions = {},\n): TabOrchestrator {\n\tconst consumers: TabConsumer[] = [];\n\tlet started = false;\n\tlet ran = false;\n\n\treturn {\n\t\tattach(consumer) {\n\t\t\tif (started) {\n\t\t\t\tthrow new Error(\"Cannot attach after run() has started\");\n\t\t\t}\n\n\t\t\tconsumers.push(consumer);\n\t\t},\n\t\tasync run() {\n\t\t\tif (ran) {\n\t\t\t\tthrow new Error(\"run() has already been called\");\n\t\t\t}\n\n\t\t\tran = true;\n\t\t\tstarted = true;\n\n\t\t\tif (consumers.length === 0) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst attached = new Set(consumers);\n\t\t\tconst screenshotSettleDelay =\n\t\t\t\toptions.screenshotSettleDelay ?? DEFAULT_SCREENSHOT_SETTLE_DELAY;\n\t\t\tconst screenshotClipBuffer = options.screenshotClipBuffer ?? DEFAULT_SCREENSHOT_CLIP_BUFFER;\n\t\t\tconst markerLimit = Math.max(\n\t\t\t\toptions.markerLimit ?? DEFAULT_MARKER_LIMIT,\n\t\t\t\toptions.baselineElementLimit ?? 0,\n\t\t\t);\n\t\t\tconst styleProps = [...FOCUS_STYLE_PROPERTIES];\n\t\t\tconst resolved = {\n\t\t\t\tscreenshotSettleDelay,\n\t\t\t\tscreenshotClipBuffer,\n\t\t\t\tstyleProps,\n\t\t\t};\n\n\t\t\tlet settleTimer: ReturnType<typeof setTimeout> | undefined;\n\t\t\tlet resolveSettle: (() => void) | undefined;\n\t\t\tconst pairByStop = { current: null as Promise<UnfocusedPair> | null };\n\t\t\tconst screenshotByStop = { current: null as Promise<Uint8Array> | null };\n\t\t\tlet activeHandle: ElementRef | undefined;\n\t\t\tlet lastHasAttributed = false;\n\t\t\t// Captured when observers are installed and re-armed after every\n\t\t\t// detected soft URL change, so repeated soft navigations across stops\n\t\t\t// compare against the most recent href rather than the original load.\n\t\t\tlet baselineHref: string | null = null;\n\n\t\t\tconst remainingHas = (capability: Capability): boolean =>\n\t\t\t\t[...attached].some((c) => c.capabilities.has(capability));\n\n\t\t\t// Decided once from who is attached at session start; never re-checked,\n\t\t\t// so the observer stays installed even after every declarer disconnects\n\t\t\t// (session-lifetime setup is never uninstalled, only skipped per-stop).\n\t\t\tconst contextSignalsWanted = remainingHas(\"contextSignals\");\n\n\t\t\tconst handleFor = (consumer: TabConsumer): TabSessionHandle => ({\n\t\t\t\tdisconnect() {\n\t\t\t\t\tattached.delete(consumer);\n\n\t\t\t\t\tif (attached.size === 0 && settleTimer !== undefined) {\n\t\t\t\t\t\tclearTimeout(settleTimer);\n\t\t\t\t\t\tsettleTimer = undefined;\n\t\t\t\t\t\tresolveSettle?.();\n\t\t\t\t\t\tresolveSettle = undefined;\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tensureUnfocusedPair: () => {\n\t\t\t\t\tif (!consumer.capabilities.has(\"unfocusedPair\")) {\n\t\t\t\t\t\tthrow new Error(\"Consumer did not declare unfocusedPair\");\n\t\t\t\t\t}\n\n\t\t\t\t\tif (!remainingHas(\"unfocusedPair\")) {\n\t\t\t\t\t\tthrow new Error(\"Consumer did not declare unfocusedPair\");\n\t\t\t\t\t}\n\n\t\t\t\t\tif (activeHandle === undefined) {\n\t\t\t\t\t\tthrow new Error(\"unfocusedPair capture not implemented\");\n\t\t\t\t\t}\n\n\t\t\t\t\tpairByStop.current ??= captureUnfocusedPair(adaptor, resolved, activeHandle);\n\n\t\t\t\t\treturn pairByStop.current;\n\t\t\t\t},\n\t\t\t\tscreenshotClip: () => {\n\t\t\t\t\tif (!consumer.capabilities.has(\"screenshot\")) {\n\t\t\t\t\t\tthrow new Error(\"Consumer did not declare screenshot\");\n\t\t\t\t\t}\n\n\t\t\t\t\tif (!remainingHas(\"screenshot\")) {\n\t\t\t\t\t\tthrow new Error(\"Consumer did not declare screenshot\");\n\t\t\t\t\t}\n\n\t\t\t\t\tif (activeHandle === undefined) {\n\t\t\t\t\t\tthrow new Error(\"screenshot capture not implemented\");\n\t\t\t\t\t}\n\n\t\t\t\t\tscreenshotByStop.current ??= captureScreenshot(adaptor, resolved, activeHandle);\n\n\t\t\t\t\treturn screenshotByStop.current;\n\t\t\t\t},\n\t\t\t});\n\n\t\t\tconst end = (reason: SessionEndReason): void => {\n\t\t\t\tfor (const consumer of attached) {\n\t\t\t\t\tconsumer.onSessionEnd?.(reason);\n\t\t\t\t}\n\n\t\t\t\tattached.clear();\n\t\t\t};\n\n\t\t\t/**\n\t\t\t * Primary navigation detection layer: compares `location.href` against\n\t\t\t * `baselineHref` on every stop, since a full navigation destroys the\n\t\t\t * in-page observer's execution context before it can self-report\n\t\t\t * anything (`drainContextObserverScript`'s own `navigation` field is\n\t\t\t * always `false`). A destroyed context can also simply throw instead\n\t\t\t * of returning a changed href (e.g. the navigation completes between\n\t\t\t * the href read and the drain read), which is treated the same way.\n\t\t\t *\n\t\t\t * Must run before the caller's `isBody`/\"done tabbing\" exit check: a\n\t\t\t * freshly-navigated page's `document.activeElement` defaults to\n\t\t\t * `document.body` unless the new page autofocuses something, so\n\t\t\t * without this proactive check a silent navigation would otherwise be\n\t\t\t * misclassified as `\"completed\"`.\n\t\t\t *\n\t\t\t * Deliberately cheap on the common (non-navigating) path: it only\n\t\t\t * reads `location.href` and does not call `drainContextObserverScript`\n\t\t\t * unless the href actually changed, so a stop that ends the session\n\t\t\t * via the isBody exit right after this check still drains at most\n\t\t\t * once overall (at the later, full-mapping call site).\n\t\t\t */\n\t\t\tasync function checkContextNavigation(): Promise<ContextNavigationOutcome> {\n\t\t\t\tlet href: string;\n\n\t\t\t\ttry {\n\t\t\t\t\thref = await adaptor.evaluate(locationHrefScript);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (!isContextDestroyedError(error)) {\n\t\t\t\t\t\tthrow error;\n\t\t\t\t\t}\n\n\t\t\t\t\treturn { navigation: true };\n\t\t\t\t}\n\n\t\t\t\tif (baselineHref === null || href === baselineHref) {\n\t\t\t\t\treturn { navigation: false, raw: null };\n\t\t\t\t}\n\n\t\t\t\t// Document navigated (or soft URL change). Prefer the observer's\n\t\t\t\t// own soft-nav flag when the execution context survived;\n\t\t\t\t// otherwise treat it as a full navigation.\n\t\t\t\tlet raw: DrainContextObserverResult | null = null;\n\n\t\t\t\ttry {\n\t\t\t\t\traw = await adaptor.evaluate(drainContextObserverScript, MARKER_ATTR);\n\t\t\t\t} catch {\n\t\t\t\t\traw = null;\n\t\t\t\t}\n\n\t\t\t\tif (raw === null || !raw.softUrlChange) {\n\t\t\t\t\treturn { navigation: true };\n\t\t\t\t}\n\n\t\t\t\tbaselineHref = href;\n\n\t\t\t\treturn { navigation: false, raw };\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Whether the page reports focus, re-asserting focus reporting once\n\t\t\t * before answering no.\n\t\t\t */\n\t\t\tasync function documentHasFocus(): Promise<boolean> {\n\t\t\t\tif (await adaptor.evaluate(hasFocusScript)) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\n\t\t\t\t// Another target taking the foreground clears focus emulation.\n\t\t\t\tawait adaptor.ensureFocusReporting();\n\n\t\t\t\treturn adaptor.evaluate(hasFocusScript);\n\t\t\t}\n\n\t\t\tlet failure: unknown;\n\n\t\t\ttry {\n\t\t\t\tawait adaptor.ensureFocusReporting();\n\n\t\t\t\tconst payload = await adaptor.evaluate(\n\t\t\t\t\tbaselineScript,\n\t\t\t\t\tstyleProps,\n\t\t\t\t\tMARKER_ATTR,\n\t\t\t\t\tmarkerLimit,\n\t\t\t\t);\n\t\t\t\tconst interned = new Map<number, StyleSnapshot>();\n\n\t\t\t\tfor (const entry of payload.entries) {\n\t\t\t\t\tconst styles = payload.styles[entry.styleIndex];\n\n\t\t\t\t\tif (styles !== undefined) {\n\t\t\t\t\t\tinterned.set(entry.index, styles);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (contextSignalsWanted) {\n\t\t\t\t\tbaselineHref = await adaptor.evaluate(locationHrefScript);\n\t\t\t\t\tawait adaptor.evaluate(installContextObserverScript);\n\t\t\t\t}\n\n\t\t\t\tfor (const consumer of [...attached]) {\n\t\t\t\t\tawait consumer.onSessionStart?.(handleFor(consumer));\n\t\t\t\t}\n\n\t\t\t\tconst visited = new Set<number>();\n\t\t\t\tlet tabIndex = 0;\n\n\t\t\t\twhile (attached.size > 0) {\n\t\t\t\t\tpairByStop.current = null;\n\t\t\t\t\tscreenshotByStop.current = null;\n\t\t\t\t\tlastHasAttributed = false;\n\n\t\t\t\t\tif (!(await documentHasFocus())) {\n\t\t\t\t\t\tend(\"lostFocus\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tawait adaptor.pressTab();\n\t\t\t\t\tawait new Promise<void>((resolve) => {\n\t\t\t\t\t\tresolveSettle = resolve;\n\t\t\t\t\t\tsettleTimer = setTimeout(() => {\n\t\t\t\t\t\t\tsettleTimer = undefined;\n\t\t\t\t\t\t\tresolveSettle = undefined;\n\t\t\t\t\t\t\tresolve();\n\t\t\t\t\t\t}, screenshotSettleDelay);\n\t\t\t\t\t});\n\n\t\t\t\t\tif (attached.size === 0) {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Chromium only matches :focus while the document reports focus, so\n\t\t\t\t\t// an element measured after a loss reads as having no indicator\n\t\t\t\t\t// however correct its CSS is. Read here, at the point of\n\t\t\t\t\t// measurement, rather than relying on the pre-tab check. Acted on\n\t\t\t\t\t// below, once the probe has said whether there was anything to\n\t\t\t\t\t// measure.\n\t\t\t\t\tconst focusedAtMeasurement = await documentHasFocus();\n\n\t\t\t\t\tlet base: ActiveElementBase | null;\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tbase = await adaptor.evaluate(probeActiveElementScript, styleProps, MARKER_ATTR);\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tif (remainingHas(\"contextSignals\") && isContextDestroyedError(error)) {\n\t\t\t\t\t\t\t// Mid-flight navigation destroyed the execution context\n\t\t\t\t\t\t\t// before the probe could even run: there is no active\n\t\t\t\t\t\t\t// element to report, so end directly (mirrors the\n\t\t\t\t\t\t\t// isBody/\"done tabbing\" exit below, which also ends\n\t\t\t\t\t\t\t// without a snapshot when there is nothing to notify).\n\t\t\t\t\t\t\tend(\"navigation\");\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tthrow error;\n\t\t\t\t\t}\n\n\t\t\t\t\t// The press that leaves the last element takes focus out of the\n\t\t\t\t\t// document with it, so an unfocused page probing as <body> is the\n\t\t\t\t\t// tab order running out, not a stolen focus. Re-asserting focus\n\t\t\t\t\t// emulation cannot bring that back either. Leave it to the isBody\n\t\t\t\t\t// exit below and treat only a real element measured without focus\n\t\t\t\t\t// as a genuine loss.\n\t\t\t\t\tif (!focusedAtMeasurement && base !== null && !base.isBody) {\n\t\t\t\t\t\tend(\"lostFocus\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Primary navigation detection layer, run before the isBody exit:\n\t\t\t\t\t// a silently-navigated page's evaluate() calls succeed against the\n\t\t\t\t\t// new document (Puppeteer/CDP targets whichever context is live at\n\t\t\t\t\t// call time), so a hard nav can surface as an ordinary `isBody: true`\n\t\t\t\t\t// probe result with no thrown error at all.\n\t\t\t\t\tlet contextNav: ContextNavigationOutcome | null = null;\n\n\t\t\t\t\tif (remainingHas(\"contextSignals\")) {\n\t\t\t\t\t\tconst outcome = await checkContextNavigation();\n\n\t\t\t\t\t\tif (outcome.navigation) {\n\t\t\t\t\t\t\tend(\"navigation\");\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tcontextNav = outcome;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (base === null || base.isBody) {\n\t\t\t\t\t\t// Focus landed on <body>. This is normally \"done tabbing\", but it\n\t\t\t\t\t\t// is also what F55 focus-removal looks like (an element receives\n\t\t\t\t\t\t// focus and then blurs itself, e.g. `onfocus=\"this.blur()\"`): the\n\t\t\t\t\t\t// in-page observer's focusin history still attributes that stop to\n\t\t\t\t\t\t// the element that briefly held focus, even though nothing is\n\t\t\t\t\t\t// focused now. Drain before ending so that case gets one last\n\t\t\t\t\t\t// notification instead of vanishing silently.\n\t\t\t\t\t\tif (remainingHas(\"contextSignals\")) {\n\t\t\t\t\t\t\tlet raw: DrainContextObserverResult;\n\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\traw =\n\t\t\t\t\t\t\t\t\tcontextNav?.raw ??\n\t\t\t\t\t\t\t\t\t(await adaptor.evaluate(drainContextObserverScript, MARKER_ATTR));\n\t\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\t\tif (!isContextDestroyedError(error)) {\n\t\t\t\t\t\t\t\t\tthrow error;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// Mirrors the probe's own destroyed-context catch above:\n\t\t\t\t\t\t\t\t// there is no attributed element left to report, so end\n\t\t\t\t\t\t\t\t// directly with \"navigation\" rather than \"completed\" (a\n\t\t\t\t\t\t\t\t// navigation signal always ends the session that way).\n\t\t\t\t\t\t\t\tend(\"navigation\");\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif (raw.hasAttributed) {\n\t\t\t\t\t\t\t\tconst attributedRef = await adaptor.evaluateHandle(attributedHandleScript);\n\t\t\t\t\t\t\t\tlet selector: string;\n\n\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\tselector = await adaptor.evaluate(getSelector, attributedRef);\n\t\t\t\t\t\t\t\t} finally {\n\t\t\t\t\t\t\t\t\tawait adaptor.disposeRef(attributedRef);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tconst activeElement: ActiveElementInfo = {\n\t\t\t\t\t\t\t\t\tindex: null,\n\t\t\t\t\t\t\t\t\tisBody: false,\n\t\t\t\t\t\t\t\t\tisIframe: false,\n\t\t\t\t\t\t\t\t\tselector,\n\t\t\t\t\t\t\t\t\thtml: truncateHtml(raw.attributedHtml ?? \"\"),\n\t\t\t\t\t\t\t\t\tstyles: { element: {}, before: {}, after: {} },\n\t\t\t\t\t\t\t\t\trect: { x: 0, y: 0, width: 0, height: 0 },\n\t\t\t\t\t\t\t\t};\n\n\t\t\t\t\t\t\t\tconst snapshot: TabStopSnapshot = {\n\t\t\t\t\t\t\t\t\ttabIndex: ++tabIndex,\n\t\t\t\t\t\t\t\t\tactiveElement,\n\t\t\t\t\t\t\t\t\tcontextSignals: {\n\t\t\t\t\t\t\t\t\t\tsignals: {\n\t\t\t\t\t\t\t\t\t\t\topenedWindow: raw.openedWindow,\n\t\t\t\t\t\t\t\t\t\t\tsubmittedForm: raw.submittedForm,\n\t\t\t\t\t\t\t\t\t\t\tfocusRemoved: raw.focusRemoved,\n\t\t\t\t\t\t\t\t\t\t\tredirect: raw.redirect,\n\t\t\t\t\t\t\t\t\t\t\tsoftUrlChange: raw.softUrlChange,\n\t\t\t\t\t\t\t\t\t\t\tnavigation: raw.navigation,\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t};\n\n\t\t\t\t\t\t\t\t// This synthetic stop has no live focused element (it\n\t\t\t\t\t\t\t\t// already blurred itself before this notification is\n\t\t\t\t\t\t\t\t// built, by definition of F55), so capabilities that\n\t\t\t\t\t\t\t\t// need one (`unfocusedPair`, `baselineStyles`,\n\t\t\t\t\t\t\t\t// `obscuring`) have nothing to meaningfully act on.\n\t\t\t\t\t\t\t\t// Only consumers that declared `contextSignals` (the\n\t\t\t\t\t\t\t\t// capability this whole synthetic-stop mechanism exists\n\t\t\t\t\t\t\t\t// to serve) are notified; unlike every other stop in\n\t\t\t\t\t\t\t\t// this file, uniform notification is wrong here because\n\t\t\t\t\t\t\t\t// there is no element for the other capabilities to\n\t\t\t\t\t\t\t\t// read from.\n\t\t\t\t\t\t\t\tconst recipients = [...attached].filter((consumer) =>\n\t\t\t\t\t\t\t\t\tconsumer.capabilities.has(\"contextSignals\"),\n\t\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\tfor (const consumer of recipients) {\n\t\t\t\t\t\t\t\t\t\tawait consumer.onTabStop(snapshot, handleFor(consumer));\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t} finally {\n\t\t\t\t\t\t\t\t\t// Guaranteed even if a recipient's onTabStop throws,\n\t\t\t\t\t\t\t\t\t// so the `data-a11y-ctx-attr` DOM marker never survives\n\t\t\t\t\t\t\t\t\t// past this stop (mirrors how `disposeRef` is\n\t\t\t\t\t\t\t\t\t// guaranteed via `finally` in the main per-stop loop\n\t\t\t\t\t\t\t\t\t// below).\n\t\t\t\t\t\t\t\t\tawait adaptor.evaluate(clearAttributedScript);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tend(\"completed\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (base.isIframe) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (base.index !== null) {\n\t\t\t\t\t\tif (visited.has(base.index)) {\n\t\t\t\t\t\t\tend(\"completed\");\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tvisited.add(base.index);\n\t\t\t\t\t}\n\n\t\t\t\t\tconst ref = await adaptor.evaluateHandle(activeElementHandleScript);\n\t\t\t\t\tactiveHandle = ref;\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst selector = await adaptor.evaluate(getSelector, ref);\n\n\t\t\t\t\t\tconst activeElement: ActiveElementInfo = {\n\t\t\t\t\t\t\t...base,\n\t\t\t\t\t\t\thtml: truncateHtml(base.html),\n\t\t\t\t\t\t\tselector,\n\t\t\t\t\t\t};\n\n\t\t\t\t\t\tconst snapshot: TabStopSnapshot = {\n\t\t\t\t\t\t\ttabIndex: ++tabIndex,\n\t\t\t\t\t\t\tactiveElement,\n\t\t\t\t\t\t};\n\n\t\t\t\t\t\tif (remainingHas(\"baselineStyles\") && activeElement.index !== null) {\n\t\t\t\t\t\t\tconst baselineStyles = interned.get(activeElement.index);\n\n\t\t\t\t\t\t\tif (baselineStyles !== undefined) {\n\t\t\t\t\t\t\t\tsnapshot.baselineStyles = baselineStyles;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (remainingHas(\"contextSignals\")) {\n\t\t\t\t\t\t\tlet signals: ContextChangeSignals;\n\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t// Reuse the drain already fetched above while resolving\n\t\t\t\t\t\t\t\t// a soft URL change (checkContextNavigation), if any;\n\t\t\t\t\t\t\t\t// re-draining here would read back state that call\n\t\t\t\t\t\t\t\t// already reset (focusIns, softUrlChange, etc.) and\n\t\t\t\t\t\t\t\t// silently lose whatever it was reporting. Otherwise\n\t\t\t\t\t\t\t\t// (the common case: href unchanged, nothing drained\n\t\t\t\t\t\t\t\t// yet) do the one drain this stop needs.\n\t\t\t\t\t\t\t\tconst raw =\n\t\t\t\t\t\t\t\t\tcontextNav?.raw ??\n\t\t\t\t\t\t\t\t\t(await adaptor.evaluate(drainContextObserverScript, MARKER_ATTR));\n\t\t\t\t\t\t\t\tsignals = {\n\t\t\t\t\t\t\t\t\topenedWindow: raw.openedWindow,\n\t\t\t\t\t\t\t\t\tsubmittedForm: raw.submittedForm,\n\t\t\t\t\t\t\t\t\tfocusRemoved: raw.focusRemoved,\n\t\t\t\t\t\t\t\t\tredirect: raw.redirect,\n\t\t\t\t\t\t\t\t\tsoftUrlChange: raw.softUrlChange,\n\t\t\t\t\t\t\t\t\tnavigation: raw.navigation,\n\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\tlastHasAttributed = raw.hasAttributed;\n\t\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\t\tif (!isContextDestroyedError(error)) {\n\t\t\t\t\t\t\t\t\tthrow error;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// The page navigated out from under the drain call: the\n\t\t\t\t\t\t\t\t// in-page observer's own state (and any DOM markers it\n\t\t\t\t\t\t\t\t// set) went with it, so there is nothing left to report\n\t\t\t\t\t\t\t\t// besides the navigation itself.\n\t\t\t\t\t\t\t\tsignals = {\n\t\t\t\t\t\t\t\t\topenedWindow: false,\n\t\t\t\t\t\t\t\t\tsubmittedForm: false,\n\t\t\t\t\t\t\t\t\tfocusRemoved: false,\n\t\t\t\t\t\t\t\t\tredirect: null,\n\t\t\t\t\t\t\t\t\tsoftUrlChange: false,\n\t\t\t\t\t\t\t\t\tnavigation: true,\n\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\tlastHasAttributed = false;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tsnapshot.contextSignals = { signals };\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (remainingHas(\"obscuring\")) {\n\t\t\t\t\t\t\tlet raw = await adaptor.evaluate(measureObscuringScript, ref, OBSCURER_ATTR);\n\n\t\t\t\t\t\t\tif (raw.fullyObscured) {\n\t\t\t\t\t\t\t\tawait new Promise((resolve) => setTimeout(resolve, OBSCURING_RECHECK_DELAY_MS));\n\t\t\t\t\t\t\t\traw = await adaptor.evaluate(measureObscuringScript, ref, OBSCURER_ATTR);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tlet obscuredBy: { selector: string; html: string } | null = null;\n\n\t\t\t\t\t\t\tif (raw.hasObscurer) {\n\t\t\t\t\t\t\t\tconst obscurer = await adaptor.evaluateHandle(obscurerHandleScript);\n\n\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\tconst obscurerSelector = await adaptor.evaluate(getSelector, obscurer);\n\t\t\t\t\t\t\t\t\tobscuredBy = {\n\t\t\t\t\t\t\t\t\t\tselector: obscurerSelector,\n\t\t\t\t\t\t\t\t\t\thtml: truncateHtml(raw.obscuredByHtml ?? \"\"),\n\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t} finally {\n\t\t\t\t\t\t\t\t\tawait adaptor.disposeRef(obscurer);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tsnapshot.obscuring = {\n\t\t\t\t\t\t\t\tcoveredFraction: raw.coveredFraction,\n\t\t\t\t\t\t\t\tfullyObscured: raw.fullyObscured,\n\t\t\t\t\t\t\t\toffscreen: raw.offscreen,\n\t\t\t\t\t\t\t\topacity: raw.opacity,\n\t\t\t\t\t\t\t\tobscuredBy,\n\t\t\t\t\t\t\t};\n\n\t\t\t\t\t\t\tawait adaptor.evaluate(clearObscurerScript, OBSCURER_ATTR);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst recipients = [...attached];\n\n\t\t\t\t\t\tfor (const consumer of recipients) {\n\t\t\t\t\t\t\tawait consumer.onTabStop(snapshot, handleFor(consumer));\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (snapshot.contextSignals?.signals.navigation) {\n\t\t\t\t\t\t\tend(\"navigation\");\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t} finally {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tif (pairByStop.current !== null) {\n\t\t\t\t\t\t\t\tawait pairByStop.current;\n\n\t\t\t\t\t\t\t\tif (contextSignalsWanted) {\n\t\t\t\t\t\t\t\t\t// ensureUnfocusedPair blurred and re-focused the element to\n\t\t\t\t\t\t\t\t\t// capture the unfocused screenshot; drop that noise so the\n\t\t\t\t\t\t\t\t\t// next drain doesn't misread it as a real focus-removal or\n\t\t\t\t\t\t\t\t\t// redirect.\n\t\t\t\t\t\t\t\t\tawait adaptor.evaluate(clearContextFocusInsScript);\n\n\t\t\t\t\t\t\t\t\tif (lastHasAttributed) {\n\t\t\t\t\t\t\t\t\t\tawait adaptor.evaluate(clearAttributedScript);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t// Capture errors already surface through the consumer that\n\t\t\t\t\t\t\t// awaited; still dispose the handle below.\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tactiveHandle = undefined;\n\t\t\t\t\t\tawait adaptor.disposeRef(ref);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tfailure = error;\n\t\t\t} finally {\n\t\t\t\ttry {\n\t\t\t\t\tawait adaptor.evaluate(clearMarkersScript, MARKER_ATTR);\n\t\t\t\t} catch {\n\t\t\t\t\t// Isolate teardown so a marker-clear throw cannot replace the loop error.\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (failure !== undefined) {\n\t\t\t\tend(\"failed\");\n\t\t\t\tthrow failure;\n\t\t\t}\n\t\t},\n\t};\n}\n","import type { BrowserAdaptor, ElementRef } from \"@a11y-pulse/browser-adaptor\";\nimport { bufferedClip } from \"@a11y-pulse/browser-adaptor\";\nimport { elementRectScript, pageDimensionsScript } from \"./browser-scripts\";\n\nexport type CaptureScreenshotOptions = {\n\tscreenshotClipBuffer: number;\n};\n\n/**\n * Screenshot the element `handle` currently refers to, clipped to its box plus\n * `screenshotClipBuffer` padding. The caller owns `handle` and must dispose it after this promise\n * settles.\n */\nexport async function captureScreenshot(\n\tadaptor: BrowserAdaptor,\n\toptions: CaptureScreenshotOptions,\n\thandle: ElementRef,\n): Promise<Uint8Array> {\n\tconst { width: pageWidth, height: pageHeight } = await adaptor.evaluate(pageDimensionsScript);\n\tconst rect = await adaptor.evaluate(elementRectScript, handle);\n\tconst clip = bufferedClip(rect, pageWidth, pageHeight, options.screenshotClipBuffer);\n\tconst scale = adaptor.screenshotClipScale ?? 1;\n\n\treturn adaptor.screenshotClip(clip, scale);\n}\n","import type { BrowserAdaptor, ElementRef } from \"@a11y-pulse/browser-adaptor\";\nimport { bufferedClip } from \"@a11y-pulse/browser-adaptor\";\nimport {\n\tblurScript,\n\telementRectScript,\n\telementStylesScript,\n\tfocusScript,\n\tneedsCentringScript,\n\tpageDimensionsScript,\n\tscrollToCenterScript,\n} from \"./browser-scripts\";\nimport type { UnfocusedPair } from \"./types\";\n\nexport type CaptureUnfocusedPairOptions = {\n\tscreenshotSettleDelay: number;\n\tscreenshotClipBuffer: number;\n\tstyleProps: readonly string[];\n};\n\n/**\n * Capture focused and unfocused screenshots of the same element, plus the unfocused style snapshot\n * and clip anchors appearance needs to score later.\n *\n * The caller owns `handle` and must dispose it after this promise settles.\n */\nexport async function captureUnfocusedPair(\n\tadaptor: BrowserAdaptor,\n\toptions: CaptureUnfocusedPairOptions,\n\thandle: ElementRef,\n): Promise<UnfocusedPair> {\n\t// Tab scrolls an element just barely into view at the viewport edge, which is exactly where fixed\n\t// overlays (cookie banners, sticky footers) sit. If something covers the element there, both\n\t// screenshots would show the overlay and no indicator could ever be detected. The clip's padding\n\t// also hangs past the edge there, and a screenshot only captures what is inside the viewport, so\n\t// an indicator drawn in that padding would be lost. Centre the element in the viewport first in\n\t// either case, then give the scroll a moment to settle.\n\tif (await adaptor.evaluate(needsCentringScript, handle, options.screenshotClipBuffer)) {\n\t\tawait adaptor.evaluate(scrollToCenterScript, handle, options.screenshotClipBuffer);\n\n\t\tawait new Promise((resolve) => setTimeout(resolve, options.screenshotSettleDelay));\n\t}\n\n\tconst { width: pageWidth, height: pageHeight } = await adaptor.evaluate(pageDimensionsScript);\n\n\tconst focusedRect = await adaptor.evaluate(elementRectScript, handle);\n\tconst focusedClip = bufferedClip(\n\t\tfocusedRect,\n\t\tpageWidth,\n\t\tpageHeight,\n\t\toptions.screenshotClipBuffer,\n\t);\n\n\tconst scale = adaptor.screenshotClipScale ?? 1;\n\n\t// Capture the focused state first, while focus is genuine: re-focusing the element afterwards\n\t// cannot always restore it (the host of a closed shadow root cannot push focus back inside), so\n\t// the focused frame must be taken before blurring.\n\tconst focusedScreenshot = await adaptor.screenshotClip(focusedClip, scale);\n\n\tawait adaptor.evaluate(blurScript, handle);\n\n\t// Measure the unfocused rect after blurring rather than reusing a stale baseline rect: a :focus\n\t// rule may have moved the element, and a fixed-position element's page-relative rect changes with\n\t// every scroll.\n\tconst unfocusedRect = await adaptor.evaluate(elementRectScript, handle);\n\tconst unfocusedClip = bufferedClip(\n\t\tunfocusedRect,\n\t\tpageWidth,\n\t\tpageHeight,\n\t\toptions.screenshotClipBuffer,\n\t);\n\n\tconst unfocusedScreenshot = await adaptor.screenshotClip(unfocusedClip, scale);\n\n\tconst unfocusedStyles = await adaptor.evaluate(elementStylesScript, handle, [\n\t\t...options.styleProps,\n\t]);\n\n\t// Restore focus so the next Tab advances from this element rather than restarting traversal. (A\n\t// closed-shadow host can't be re-focused into the root; the loop still progresses because Tab\n\t// re-enters from there.)\n\tawait adaptor.evaluate(focusScript, handle);\n\n\treturn {\n\t\tfocusedScreenshot,\n\t\tunfocusedScreenshot,\n\t\tunfocusedStyles,\n\t\tfocusedRect,\n\t\tunfocusedRect,\n\t\tfocusedClip,\n\t\tunfocusedClip,\n\t\tscale,\n\t};\n}\n"],"mappings":"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","names":["opacity","bufferedClip","bufferedClip","DEFAULT_SCREENSHOT_SETTLE_DELAY"]}
|
|
1
|
+
{"version":3,"sources":["../src/audit-scaffold.ts","../src/browser-scripts.ts","../src/focus-style.ts","../src/orchestrator.ts","../src/capture-screenshot.ts","../src/unfocused-pair.ts"],"sourcesContent":["import type { SessionEndReason, TabSessionHandle } from \"./types\";\n\nexport const DEFAULT_ELEMENT_LIMIT = 1024;\nexport const DEFAULT_SCREENSHOT_SETTLE_DELAY = 33; // ~2 frames at 60fps\nexport const DEFAULT_FAILED_ELEMENT_LIMIT = 0; // 0 = never finish early\nexport const DEFAULT_TIMEOUT = 0; // 0 = no timeout\n\nexport type BaseAuditOptions = {\n\t/** Max focusable elements to tab through. */\n\telementLimit?: number;\n\t/** How long to wait (ms) after each Tab for the page to settle. */\n\tscreenshotSettleDelay?: number;\n\t/** Disconnect early after this many elements fail (0 = never). */\n\tfailedElementLimit?: number;\n\t/** Return whatever's gathered so far after this many ms (0 = no timeout). */\n\ttimeout?: number;\n};\n\nexport type BaseAuditSummary = {\n\tchecked: number;\n\tpassed: number;\n\tfailed: number;\n\t/** True if the element limit was hit before tabbing finished. */\n\treachedLimit: boolean;\n\t/** True if the audit stopped early after hitting `failedElementLimit`. */\n\treachedFailedElementLimit: boolean;\n\t/**\n\t * True if the audit returned early because `timeout` elapsed. The results are whatever had been\n\t * gathered when the deadline hit.\n\t */\n\ttimedOut: boolean;\n\t/**\n\t * Why the tab session ended, or `null` when this consumer disconnected itself (element limit,\n\t * failed-element limit, or timeout).\n\t */\n\tsessionEnd: SessionEndReason | null;\n};\n\nexport type AuditSelfDisconnect = {\n\t/** True once this consumer has disconnected itself from the session. */\n\treadonly disconnectedSelf: boolean;\n\t/** Arm a timeout that calls `onTimeout` then disconnects, if `timeout` is greater than 0. */\n\tarmTimeout(timeout: number, session: TabSessionHandle, onTimeout: () => void): void;\n\t/** Disconnect the session and record that this consumer initiated it. */\n\tdisconnect(session: TabSessionHandle): void;\n\t/** Clear any pending timeout without disconnecting. */\n\tclear(): void;\n};\n\n/**\n * Arms an optional self-disconnecting timeout and tracks whether this consumer initiated its own\n * disconnect, so `onSessionEnd` can tell that apart from an external end.\n */\nexport function createAuditSelfDisconnect(): AuditSelfDisconnect {\n\tlet timer: ReturnType<typeof setTimeout> | undefined;\n\tlet disconnectedSelf = false;\n\n\tconst clear = (): void => {\n\t\tif (timer !== undefined) {\n\t\t\tclearTimeout(timer);\n\t\t\ttimer = undefined;\n\t\t}\n\t};\n\n\tconst disconnect = (session: TabSessionHandle): void => {\n\t\tdisconnectedSelf = true;\n\t\tclear();\n\t\tsession.disconnect();\n\t};\n\n\treturn {\n\t\tget disconnectedSelf() {\n\t\t\treturn disconnectedSelf;\n\t\t},\n\t\tarmTimeout(timeout, session, onTimeout) {\n\t\t\tif (timeout > 0) {\n\t\t\t\ttimer = setTimeout(() => {\n\t\t\t\t\ttimer = undefined;\n\t\t\t\t\tonTimeout();\n\t\t\t\t\tdisconnect(session);\n\t\t\t\t}, timeout);\n\t\t\t}\n\t\t},\n\t\tdisconnect,\n\t\tclear,\n\t};\n}\n","/**\n * IMPORTANT: Functions in this file are serialized and injected into the audited page, so they must\n * not reference any symbols outside their own scope. This also means they cannot share code, so any\n * common logic (like descending into shadow roots) must be duplicated.\n */\n\nimport type { Rect } from \"@a11y-pulse/browser-adaptor\";\nimport type { StyleSnapshot } from \"./types\";\n\ntype BaselineElementEntry = {\n\tindex: number;\n\t/** Position of this element's snapshot in the payload's `styles` table. */\n\tstyleIndex: number;\n\t/** The element's page-relative rect in its unfocused (baseline) state. */\n\trect: Rect;\n};\n\n/**\n * The baseline as a table of unique style snapshots plus per-element entries referencing them.\n * Elements overwhelmingly share identical snapshots (same classes), so interning keeps the\n * serialised payload O(unique styles) instead of O(elements × style size).\n */\nexport type BaselinePayload = {\n\tstyles: StyleSnapshot[];\n\tentries: BaselineElementEntry[];\n};\n\n/**\n * Find up to `limit` focusable elements and mark them with `markerAttr` and a unique index. Then\n * return a snapshot of their styles to use as a baseline.\n */\nexport function baselineScript(\n\tprops: string[],\n\tmarkerAttr: string,\n\tlimit: number,\n): BaselinePayload {\n\t// Bound oversized computed values (data-URI background images can run to hundreds of KB) while\n\t// preserving inequality via the length and a fingerprint of the full value. Must be\n\t// byte-identical to the copy in probeActiveElementScript, or every oversized value would \"change\"\n\t// on focus and produce a false style-check pass.\n\tconst truncateValue = (value: string): string => {\n\t\tconst MAX_STYLE_VALUE_LENGTH = 512;\n\n\t\tif (value.length <= MAX_STYLE_VALUE_LENGTH) {\n\t\t\treturn value;\n\t\t}\n\n\t\tlet hash = 5381;\n\n\t\tfor (let i = 0; i < value.length; i++) {\n\t\t\thash = ((hash * 33) ^ value.charCodeAt(i)) >>> 0;\n\t\t}\n\n\t\treturn `${value.slice(0, MAX_STYLE_VALUE_LENGTH)}#len=${value.length}#h=${hash.toString(36)}`;\n\t};\n\n\tconst snapshot = (el: Element): StyleSnapshot => {\n\t\tconst read = (pseudo: string | undefined): Record<string, string> => {\n\t\t\tconst cs = getComputedStyle(el, pseudo);\n\t\t\tconst out: Record<string, string> = {};\n\n\t\t\tfor (const prop of props) {\n\t\t\t\tout[prop] = truncateValue(cs.getPropertyValue(prop));\n\t\t\t}\n\n\t\t\tif (pseudo) {\n\t\t\t\tout.content = truncateValue(cs.getPropertyValue(\"content\"));\n\t\t\t}\n\n\t\t\treturn out;\n\t\t};\n\n\t\treturn {\n\t\t\telement: read(undefined),\n\t\t\tbefore: read(\"::before\"),\n\t\t\tafter: read(\"::after\"),\n\t\t};\n\t};\n\n\tconst candidates: Element[] = [];\n\tconst focusableSelector = [\n\t\t\"a[href]\",\n\t\t\"button\",\n\t\t\"input\",\n\t\t\"select\",\n\t\t\"textarea\",\n\t\t\"[tabindex]\",\n\t\t'[contenteditable=\"\"]',\n\t\t'[contenteditable=\"true\"]',\n\t].join(\", \");\n\n\t// Collect focusable candidates in document order, descending into open shadow roots.\n\tconst collect = (root: Document | ShadowRoot): void => {\n\t\tfor (const el of Array.from(root.querySelectorAll(\"*\"))) {\n\t\t\tif (el.matches(focusableSelector)) {\n\t\t\t\tcandidates.push(el);\n\t\t\t}\n\n\t\t\tif (el.shadowRoot) {\n\t\t\t\tcollect(el.shadowRoot);\n\t\t\t}\n\t\t}\n\t};\n\n\tcollect(document);\n\n\tconst styles: StyleSnapshot[] = [];\n\tconst styleIndexByKey = new Map<string, number>();\n\tconst entries: BaselineElementEntry[] = [];\n\tlet idx = 0;\n\n\tfor (const el of candidates) {\n\t\tif (idx >= limit) {\n\t\t\tbreak;\n\t\t}\n\n\t\tif (el.hasAttribute(\"disabled\")) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tif ((el as HTMLElement).tabIndex < 0) {\n\t\t\t// Skip elements that are focusable but not in the tab order (negative tabindex). The audit\n\t\t\t// tabs through the page, so it never lands on them, and counting them would waste the\n\t\t\t// element-limit budget.\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Skip non-visible elements so they don't consume the marker budget (e.g. links inside a closed\n\t\t// mega-menu). checkVisibility sees hiding applied by ancestors, which per-element computed\n\t\t// styles don't (`display` doesn't inherit); fall back to the element's own styles where it's\n\t\t// unavailable.\n\t\tconst visibilityCheck = el as Element & {\n\t\t\tcheckVisibility?: (options?: { checkVisibilityCSS?: boolean }) => boolean;\n\t\t};\n\n\t\tif (typeof visibilityCheck.checkVisibility === \"function\") {\n\t\t\tif (!visibilityCheck.checkVisibility({ checkVisibilityCSS: true })) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t} else {\n\t\t\tconst cs = getComputedStyle(el);\n\n\t\t\tif (cs.display === \"none\" || cs.visibility === \"hidden\") {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\t// Mark before measuring: the probe reads styles and rects while the marker is present, so the\n\t\t// baseline must be captured in the same DOM state.\n\t\tel.setAttribute(markerAttr, String(idx));\n\n\t\tconst rect = el.getBoundingClientRect();\n\n\t\t// Intern the snapshot: the JSON key is deterministic because every snapshot is built with the\n\t\t// same property insertion order.\n\t\tconst snap = snapshot(el);\n\t\tconst key = JSON.stringify(snap);\n\t\tlet styleIndex = styleIndexByKey.get(key);\n\n\t\tif (styleIndex === undefined) {\n\t\t\tstyleIndex = styles.length;\n\t\t\tstyles.push(snap);\n\t\t\tstyleIndexByKey.set(key, styleIndex);\n\t\t}\n\n\t\tentries.push({\n\t\t\tindex: idx,\n\t\t\tstyleIndex,\n\t\t\trect: {\n\t\t\t\tx: rect.left + window.scrollX,\n\t\t\t\ty: rect.top + window.scrollY,\n\t\t\t\twidth: rect.width,\n\t\t\t\theight: rect.height,\n\t\t\t},\n\t\t});\n\t\tidx++;\n\t}\n\n\treturn { styles, entries };\n}\n\n/** Every element that can be a sequential focus stop, before tabindex, state and visibility. */\nexport const TABBABLE_SELECTOR = [\n\t\"a[href]\",\n\t\"area[href]\",\n\t\"button\",\n\t\"input:not([type='hidden'])\",\n\t\"select\",\n\t\"textarea\",\n\t\"iframe\",\n\t\"summary\",\n\t\"audio[controls]\",\n\t\"video[controls]\",\n\t\"[tabindex]\",\n\t\"[contenteditable]:not([contenteditable='false'])\",\n].join(\", \");\n\ntype TabObserverWindow = Window & {\n\t__a11yPulseTabListener?: (event: KeyboardEvent) => void;\n\t__a11yPulseLastTab?: KeyboardEvent;\n};\n\n/**\n * Keep the most recent Tab key event. The listener sits on the window in the capture phase, so the\n * page cannot stop it seeing the event, and `defaultPrevented` is read once dispatch has finished.\n */\nexport function installTabObserverScript(): void {\n\tconst win = window as TabObserverWindow;\n\n\tif (win.__a11yPulseTabListener) {\n\t\treturn;\n\t}\n\n\twin.__a11yPulseTabListener = (event) => {\n\t\tif (event.key === \"Tab\") {\n\t\t\twin.__a11yPulseLastTab = event;\n\t\t}\n\t};\n\twindow.addEventListener(\"keydown\", win.__a11yPulseTabListener, true);\n}\n\n/** Whether the page cancelled the most recent Tab key press, as seen by `installTabObserverScript`. */\nexport function tabPreventedScript(): boolean {\n\treturn (window as TabObserverWindow).__a11yPulseLastTab?.defaultPrevented === true;\n}\n\nexport function removeTabObserverScript(): void {\n\tconst win = window as TabObserverWindow;\n\n\tif (win.__a11yPulseTabListener) {\n\t\twindow.removeEventListener(\"keydown\", win.__a11yPulseTabListener, true);\n\t}\n\n\tdelete win.__a11yPulseTabListener;\n\tdelete win.__a11yPulseLastTab;\n}\n\nexport type HeldFocus = {\n\t/** The page cancelled the Tab press that left focus where it was. */\n\tprevented: boolean;\n\t/** Something tabbable follows the focused element in document order, so the tab order goes on. */\n\tfollowed: boolean;\n};\n\n/**\n * Why a Tab press left focus on the same element: the page cancelled it, or there is nothing\n * tabbable after the element to move to.\n */\nexport function heldFocusScript(tabbableSelector: string): HeldFocus {\n\tlet el = document.activeElement;\n\n\twhile (el?.shadowRoot?.activeElement) {\n\t\tel = el.shadowRoot.activeElement;\n\t}\n\n\t// Compare light-DOM positions: an element inside a shadow root stands in for its outermost host.\n\tlet anchor: Element | null = el;\n\n\twhile (anchor && anchor.getRootNode() instanceof ShadowRoot) {\n\t\tanchor = (anchor.getRootNode() as ShadowRoot).host;\n\t}\n\n\tconst modal = document.querySelector(\"dialog:modal\");\n\n\tconst isTabbable = (candidate: Element): boolean => {\n\t\tif (\n\t\t\t(candidate as HTMLElement).tabIndex < 0 ||\n\t\t\tcandidate.matches(\":disabled\") ||\n\t\t\tcandidate.closest(\"[inert]\") ||\n\t\t\t(modal && !modal.contains(candidate))\n\t\t) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst visibility = candidate as Element & {\n\t\t\tcheckVisibility?: (options?: { checkVisibilityCSS?: boolean }) => boolean;\n\t\t};\n\n\t\tif (typeof visibility.checkVisibility === \"function\") {\n\t\t\treturn visibility.checkVisibility({ checkVisibilityCSS: true });\n\t\t}\n\n\t\treturn candidate.getClientRects().length > 0;\n\t};\n\n\tconst prevented = (window as TabObserverWindow).__a11yPulseLastTab?.defaultPrevented === true;\n\tconst followed =\n\t\tanchor !== null &&\n\t\tArray.from(document.querySelectorAll(tabbableSelector)).some(\n\t\t\t(candidate) =>\n\t\t\t\tcandidate !== anchor &&\n\t\t\t\t!anchor.contains(candidate) &&\n\t\t\t\t(anchor.compareDocumentPosition(candidate) & Node.DOCUMENT_POSITION_FOLLOWING) !== 0 &&\n\t\t\t\tisTabbable(candidate),\n\t\t);\n\n\treturn { prevented, followed };\n}\n\n/**\n * Give the focused element (descending open shadow roots) the marker `index` unless it already has\n * one, and return its marker.\n */\nexport function markActiveElementScript(markerAttr: string, index: number): number {\n\tlet el = document.activeElement;\n\n\twhile (el?.shadowRoot?.activeElement) {\n\t\tel = el.shadowRoot.activeElement;\n\t}\n\n\tconst existing = el?.getAttribute(markerAttr);\n\n\tif (existing !== null && existing !== undefined) {\n\t\treturn Number(existing);\n\t}\n\n\tel?.setAttribute(markerAttr, String(index));\n\n\treturn index;\n}\n\nexport type ActiveElementBase = {\n\tindex: number | null;\n\tisBody: boolean;\n\t/**\n\t * The focused element is an <iframe>. Tabbing into a frame passes focus into its embedded\n\t * document, but the parent's document.activeElement is the frame element itself, so the loop uses\n\t * this to skip the frame as a tab stop.\n\t */\n\tisIframe: boolean;\n\thtml: string;\n\tstyles: StyleSnapshot;\n\trect: Rect;\n\t/**\n\t * The element sits on a fixed or sticky ancestor (or is one), so its page position depends on\n\t * scrolling rather than on where it is in the page.\n\t */\n\tpinned?: boolean;\n\t/** The element is inside an open dialog, where focus management may legitimately leave the page order. */\n\tinDialog?: boolean;\n};\n\n/**\n * Read the currently focused element (descending open shadow roots), returning its marker index,\n * opening-tag HTML (marker attribute stripped), focus-relevant styles, and page-relative rect.\n */\nexport function probeActiveElementScript(props: string[], markerAttr: string): ActiveElementBase {\n\tlet el = document.activeElement;\n\n\twhile (el?.shadowRoot?.activeElement) {\n\t\tel = el.shadowRoot.activeElement;\n\t}\n\n\tif (!el || el === document.body) {\n\t\treturn {\n\t\t\tindex: null,\n\t\t\tisBody: true,\n\t\t\tisIframe: false,\n\t\t\thtml: \"\",\n\t\t\tstyles: { element: {}, before: {}, after: {} },\n\t\t\trect: { x: 0, y: 0, width: 0, height: 0 },\n\t\t};\n\t}\n\n\t// Must be byte-identical to the copy in baselineScript: snapshots from the two scripts are\n\t// compared for equality, so they have to truncate oversized values the same way.\n\tconst truncateValue = (value: string): string => {\n\t\tconst MAX_STYLE_VALUE_LENGTH = 512;\n\n\t\tif (value.length <= MAX_STYLE_VALUE_LENGTH) {\n\t\t\treturn value;\n\t\t}\n\n\t\tlet hash = 5381;\n\n\t\tfor (let i = 0; i < value.length; i++) {\n\t\t\thash = ((hash * 33) ^ value.charCodeAt(i)) >>> 0;\n\t\t}\n\n\t\treturn `${value.slice(0, MAX_STYLE_VALUE_LENGTH)}#len=${value.length}#h=${hash.toString(36)}`;\n\t};\n\n\tconst read = (pseudo: string | undefined): Record<string, string> => {\n\t\tconst cs = getComputedStyle(el as Element, pseudo);\n\t\tconst out: Record<string, string> = {};\n\n\t\tfor (const prop of props) {\n\t\t\tout[prop] = truncateValue(cs.getPropertyValue(prop));\n\t\t}\n\n\t\tif (pseudo) {\n\t\t\tout.content = truncateValue(cs.getPropertyValue(\"content\"));\n\t\t}\n\n\t\treturn out;\n\t};\n\n\tconst idxAttr = el.getAttribute(markerAttr);\n\tconst rect = el.getBoundingClientRect();\n\n\t// Walks out through shadow roots, so content inside a fixed host (a chat launcher) counts too.\n\tlet pinned = false;\n\n\tfor (let node: Element | null = el; node && !pinned; ) {\n\t\tconst { position } = getComputedStyle(node);\n\t\tpinned = position === \"fixed\" || position === \"sticky\";\n\t\tnode = node.parentElement ?? (node.getRootNode() as ShadowRoot).host ?? null;\n\t}\n\n\t// Shallow clone so only the opening tag is serialised, not the whole subtree; drop the internal\n\t// marker attribute so it never leaks into the snippet.\n\tconst openingTag = el.cloneNode(false) as Element;\n\topeningTag.removeAttribute(markerAttr);\n\n\treturn {\n\t\tindex: idxAttr === null ? null : Number(idxAttr),\n\t\tisBody: false,\n\t\tisIframe: el.tagName === \"IFRAME\",\n\t\thtml: openingTag.outerHTML,\n\t\tstyles: {\n\t\t\telement: read(undefined),\n\t\t\tbefore: read(\"::before\"),\n\t\t\tafter: read(\"::after\"),\n\t\t},\n\t\trect: {\n\t\t\tx: rect.left + window.scrollX,\n\t\t\ty: rect.top + window.scrollY,\n\t\t\twidth: rect.width,\n\t\t\theight: rect.height,\n\t\t},\n\t\tpinned,\n\t\tinDialog: el.closest(\"dialog[open], [role='dialog'], [role='alertdialog']\") !== null,\n\t};\n}\n\nexport function activeElementHandleScript(): Element | null {\n\tlet el = document.activeElement;\n\n\twhile (el?.shadowRoot?.activeElement) {\n\t\tel = el.shadowRoot.activeElement;\n\t}\n\n\treturn el;\n}\n\nexport function pageDimensionsScript(): { width: number; height: number } {\n\treturn {\n\t\twidth: document.documentElement.scrollWidth,\n\t\theight: document.documentElement.scrollHeight,\n\t};\n}\n\nexport function elementRectScript(el: Element | null): Rect {\n\tif (!el) {\n\t\treturn { x: 0, y: 0, width: 0, height: 0 };\n\t}\n\n\tconst rect = el.getBoundingClientRect();\n\n\treturn {\n\t\tx: rect.left + window.scrollX,\n\t\ty: rect.top + window.scrollY,\n\t\twidth: rect.width,\n\t\theight: rect.height,\n\t};\n}\n\n/**\n * Whether something unrelated (e.g. a fixed cookie banner) covers the element's centre point, so a\n * screenshot of the element's box would show the covering element instead.\n */\nexport function isCenterObscuredScript(el: Element | null): boolean {\n\tif (!el) {\n\t\treturn false;\n\t}\n\n\tconst rect = el.getBoundingClientRect();\n\tconst x = Math.min(Math.max(rect.left + rect.width / 2, 0), window.innerWidth - 1);\n\tconst y = Math.min(Math.max(rect.top + rect.height / 2, 0), window.innerHeight - 1);\n\n\tlet hit = document.elementFromPoint(x, y);\n\n\t// Descend into shadow roots so a hit on a shadow host resolves to the inner element.\n\twhile (hit?.shadowRoot) {\n\t\tconst inner = hit.shadowRoot.elementFromPoint(x, y);\n\n\t\tif (!inner || inner === hit) {\n\t\t\tbreak;\n\t\t}\n\n\t\thit = inner;\n\t}\n\n\tif (!hit) {\n\t\treturn false;\n\t}\n\n\treturn hit !== el && !el.contains(hit) && !hit.contains(el);\n}\n\n/**\n * Whether the element should be centred in the viewport before its screenshots are taken: either\n * something unrelated covers its centre (see isCenterObscuredScript, duplicated here because these\n * scripts cannot share code), or its screenshot clip (its box padded by `buffer`, cut off at the\n * document edges like bufferedClip) reaches past an edge of the viewport. A capture only shows what\n * is inside the viewport, so such a clip would lose an indicator drawn in the padding. A clip larger\n * than the viewport does not count, since scrolling cannot fit it anyway.\n */\nexport function needsCentringScript(el: Element | null, buffer: number): boolean {\n\tif (!el) {\n\t\treturn false;\n\t}\n\n\tconst rect = el.getBoundingClientRect();\n\tconst viewportWidth = document.documentElement.clientWidth;\n\tconst viewportHeight = document.documentElement.clientHeight;\n\n\tconst x = Math.min(Math.max(rect.left + rect.width / 2, 0), viewportWidth - 1);\n\tconst y = Math.min(Math.max(rect.top + rect.height / 2, 0), viewportHeight - 1);\n\n\tlet hit = document.elementFromPoint(x, y);\n\n\twhile (hit?.shadowRoot) {\n\t\tconst inner = hit.shadowRoot.elementFromPoint(x, y);\n\n\t\tif (!inner || inner === hit) {\n\t\t\tbreak;\n\t\t}\n\n\t\thit = inner;\n\t}\n\n\tif (hit && hit !== el && !el.contains(hit) && !hit.contains(el)) {\n\t\treturn true;\n\t}\n\n\tconst docLeft = -window.scrollX;\n\tconst docTop = -window.scrollY;\n\tconst left = Math.max(rect.left - buffer, docLeft);\n\tconst top = Math.max(rect.top - buffer, docTop);\n\tconst right = Math.min(rect.right + buffer, docLeft + document.documentElement.scrollWidth);\n\tconst bottom = Math.min(rect.bottom + buffer, docTop + document.documentElement.scrollHeight);\n\n\tif (right - left > viewportWidth || bottom - top > viewportHeight) {\n\t\treturn false;\n\t}\n\n\treturn left < 0 || top < 0 || right > viewportWidth || bottom > viewportHeight;\n}\n\n/**\n * Scroll the window so the element sits at the vertical centre of the viewport and is inside it\n * horizontally with `buffer` to spare, instantly so a screenshot taken immediately afterwards isn't\n * captured mid-animation. Scrolls the window directly rather than via scrollIntoView(): Chromium\n * moves the sequential focus navigation starting point to the element passed to scrollIntoView(),\n * so when focus cannot be handed back afterwards (a closed shadow host) the next Tab would re-enter\n * the same element instead of moving on.\n */\nexport function scrollToCenterScript(el: Element | null, buffer = 0): void {\n\tif (!el) {\n\t\treturn;\n\t}\n\n\tconst rect = el.getBoundingClientRect();\n\tconst viewportWidth = document.documentElement.clientWidth;\n\tconst top = rect.top + rect.height / 2 - document.documentElement.clientHeight / 2;\n\tlet left = 0;\n\n\tif (rect.left - buffer < 0) {\n\t\tleft = rect.left - buffer;\n\t} else if (rect.right + buffer > viewportWidth) {\n\t\tleft = Math.min(rect.right + buffer - viewportWidth, rect.left - buffer);\n\t}\n\n\twindow.scrollBy({ top, left, behavior: \"instant\" });\n}\n\nexport function blurScript(el: Element | null): void {\n\t(el as HTMLElement | null)?.blur();\n}\n\nexport function focusScript(el: Element | null): void {\n\t(el as HTMLElement | null)?.focus({ preventScroll: true });\n}\n\n/**\n * Read focus-relevant computed styles from a given element. Unlike `probeActiveElementScript`, this\n * does not use `document.activeElement`, so it still works after the node has been blurred.\n */\nexport function elementStylesScript(el: Element | null, props: string[]): StyleSnapshot {\n\tif (!el) {\n\t\treturn { element: {}, before: {}, after: {} };\n\t}\n\n\t// Must be byte-identical to the copies in baselineScript and probeActiveElementScript: snapshots\n\t// from the scripts are compared for equality, so they have to truncate oversized values the same\n\t// way.\n\tconst truncateValue = (value: string): string => {\n\t\tconst MAX_STYLE_VALUE_LENGTH = 512;\n\n\t\tif (value.length <= MAX_STYLE_VALUE_LENGTH) {\n\t\t\treturn value;\n\t\t}\n\n\t\tlet hash = 5381;\n\n\t\tfor (let i = 0; i < value.length; i++) {\n\t\t\thash = ((hash * 33) ^ value.charCodeAt(i)) >>> 0;\n\t\t}\n\n\t\treturn `${value.slice(0, MAX_STYLE_VALUE_LENGTH)}#len=${value.length}#h=${hash.toString(36)}`;\n\t};\n\n\tconst read = (pseudo: string | undefined): Record<string, string> => {\n\t\tconst cs = getComputedStyle(el, pseudo);\n\t\tconst out: Record<string, string> = {};\n\n\t\tfor (const prop of props) {\n\t\t\tout[prop] = truncateValue(cs.getPropertyValue(prop));\n\t\t}\n\n\t\tif (pseudo) {\n\t\t\tout.content = truncateValue(cs.getPropertyValue(\"content\"));\n\t\t}\n\n\t\treturn out;\n\t};\n\n\treturn {\n\t\telement: read(undefined),\n\t\tbefore: read(\"::before\"),\n\t\tafter: read(\"::after\"),\n\t};\n}\n\n/**\n * Remove the marker attribute from all elements, blur the active element, and scroll back to the\n * top. This is a best-effort attempt to reset state between audits: the tab loop scrolls later\n * elements into view as it walks the page (see scrollToCenterScript), and nothing else undoes that\n * once the session ends.\n */\nexport function clearMarkersScript(markerAttr: string): void {\n\tconst clear = (root: Document | ShadowRoot): void => {\n\t\tfor (const el of Array.from(root.querySelectorAll(`[${markerAttr}]`))) {\n\t\t\tel.removeAttribute(markerAttr);\n\t\t}\n\n\t\t// Descend into any shadow roots to clear markers within them.\n\t\tfor (const el of Array.from(root.querySelectorAll(\"*\"))) {\n\t\t\tif (el.shadowRoot) {\n\t\t\t\tclear(el.shadowRoot);\n\t\t\t}\n\t\t}\n\t};\n\n\tclear(document);\n\n\t(document.activeElement as HTMLElement | null)?.blur();\n\n\twindow.scrollTo(0, 0);\n}\n\nexport type MeasureObscuringResult = {\n\tcoveredFraction: number;\n\tfullyObscured: boolean;\n\toffscreen: boolean;\n\topacity: \"opaque\" | \"semi-transparent\" | \"unknown\";\n\tobscuredByHtml: string | null;\n\t/** True when a covering element was marked for selector resolution. */\n\thasObscurer: boolean;\n};\n\n/**\n * Hit-test whether author-created content entirely hides the focused element. Marks a single\n * covering element with `obscurerAttr` when containment confirms a full cover, so the host can\n * resolve its selector via getSelector.\n */\nexport function measureObscuringScript(\n\tel: Element | null,\n\tobscurerAttr: string,\n): MeasureObscuringResult {\n\tconst empty: MeasureObscuringResult = {\n\t\tcoveredFraction: 0,\n\t\tfullyObscured: false,\n\t\toffscreen: false,\n\t\topacity: \"opaque\",\n\t\tobscuredByHtml: null,\n\t\thasObscurer: false,\n\t};\n\n\tif (!el) {\n\t\treturn empty;\n\t}\n\n\tfor (const marked of Array.from(document.querySelectorAll(`[${obscurerAttr}]`))) {\n\t\tmarked.removeAttribute(obscurerAttr);\n\t}\n\n\tconst rect = el.getBoundingClientRect();\n\tconst left = Math.max(rect.left, 0);\n\tconst top = Math.max(rect.top, 0);\n\tconst right = Math.min(rect.right, window.innerWidth);\n\tconst bottom = Math.min(rect.bottom, window.innerHeight);\n\n\tif (right <= left || bottom <= top) {\n\t\treturn { ...empty, offscreen: true };\n\t}\n\n\tconst width = right - left;\n\tconst height = bottom - top;\n\tconst LARGE_ELEMENT_THRESHOLD_PX = 80;\n\n\tconst points: Array<{ x: number; y: number }> = [\n\t\t{ x: left, y: top },\n\t\t{ x: right - 0.5, y: top },\n\t\t{ x: left, y: bottom - 0.5 },\n\t\t{ x: right - 0.5, y: bottom - 0.5 },\n\t\t{ x: left + width / 2, y: top },\n\t\t{ x: left + width / 2, y: bottom - 0.5 },\n\t\t{ x: left, y: top + height / 2 },\n\t\t{ x: right - 0.5, y: top + height / 2 },\n\t\t{ x: left + width / 2, y: top + height / 2 },\n\t];\n\n\tif (width > LARGE_ELEMENT_THRESHOLD_PX || height > LARGE_ELEMENT_THRESHOLD_PX) {\n\t\tpoints.push(\n\t\t\t{ x: left + width * 0.25, y: top + height * 0.25 },\n\t\t\t{ x: left + width * 0.75, y: top + height * 0.25 },\n\t\t\t{ x: left + width * 0.25, y: top + height * 0.75 },\n\t\t\t{ x: left + width * 0.75, y: top + height * 0.75 },\n\t\t);\n\t}\n\n\tconst hitAt = (x: number, y: number): Element | null => {\n\t\tconst px = Math.min(Math.max(x, 0), window.innerWidth - 1);\n\t\tconst py = Math.min(Math.max(y, 0), window.innerHeight - 1);\n\t\tconst stack =\n\t\t\ttypeof document.elementsFromPoint === \"function\"\n\t\t\t\t? document.elementsFromPoint(px, py)\n\t\t\t\t: [document.elementFromPoint(px, py)].filter(Boolean);\n\n\t\tfor (let candidate of stack as Element[]) {\n\t\t\twhile (candidate?.shadowRoot) {\n\t\t\t\tconst inner = candidate.shadowRoot.elementFromPoint(px, py);\n\n\t\t\t\tif (!inner || inner === candidate) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcandidate = inner;\n\t\t\t}\n\n\t\t\tif (!candidate) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (candidate === el || el.contains(candidate) || candidate.contains(el)) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\treturn candidate;\n\t\t}\n\n\t\treturn null;\n\t};\n\n\tlet covered = 0;\n\tconst coverCounts = new Map<Element, number>();\n\n\tfor (const point of points) {\n\t\tconst cover = hitAt(point.x, point.y);\n\n\t\tif (cover) {\n\t\t\tcovered++;\n\t\t\tcoverCounts.set(cover, (coverCounts.get(cover) ?? 0) + 1);\n\t\t}\n\t}\n\n\tconst coveredFraction = points.length === 0 ? 0 : covered / points.length;\n\n\tif (coveredFraction < 1) {\n\t\treturn {\n\t\t\t...empty,\n\t\t\tcoveredFraction,\n\t\t\tfullyObscured: false,\n\t\t};\n\t}\n\n\tlet topCover: Element | null = null;\n\tlet topCount = 0;\n\n\tfor (const [candidate, count] of coverCounts) {\n\t\tif (count > topCount) {\n\t\t\ttopCover = candidate;\n\t\t\ttopCount = count;\n\t\t}\n\t}\n\n\tif (!topCover) {\n\t\treturn {\n\t\t\t...empty,\n\t\t\tcoveredFraction: 1,\n\t\t\tfullyObscured: false,\n\t\t\topacity: \"unknown\",\n\t\t};\n\t}\n\n\tconst coverRect = topCover.getBoundingClientRect();\n\tconst containsFully =\n\t\tcoverRect.left <= rect.left &&\n\t\tcoverRect.top <= rect.top &&\n\t\tcoverRect.right >= rect.right &&\n\t\tcoverRect.bottom >= rect.bottom;\n\n\tif (!containsFully) {\n\t\t// Joint multi-element cover: cannot confirm a single opaque container.\n\t\treturn {\n\t\t\tcoveredFraction: 1,\n\t\t\tfullyObscured: true,\n\t\t\toffscreen: false,\n\t\t\topacity: \"unknown\",\n\t\t\tobscuredByHtml: null,\n\t\t\thasObscurer: false,\n\t\t};\n\t}\n\n\tconst classifyOpacity = (node: Element): \"opaque\" | \"semi-transparent\" | \"unknown\" => {\n\t\tconst cs = getComputedStyle(node);\n\t\tconst opacity = Number.parseFloat(cs.opacity);\n\n\t\tif (!Number.isFinite(opacity)) {\n\t\t\treturn \"unknown\";\n\t\t}\n\n\t\tif (opacity < 1) {\n\t\t\treturn \"semi-transparent\";\n\t\t}\n\n\t\tconst bg = cs.backgroundColor;\n\t\tconst rgba = bg.match(\n\t\t\t/rgba?\\(\\s*([\\d.]+)\\s*,\\s*([\\d.]+)\\s*,\\s*([\\d.]+)(?:\\s*,\\s*([\\d.]+))?\\s*\\)/,\n\t\t);\n\n\t\tif (rgba) {\n\t\t\tconst alpha = rgba[4] === undefined ? 1 : Number.parseFloat(rgba[4]);\n\n\t\t\tif (alpha < 1) {\n\t\t\t\treturn alpha <= 0 ? \"semi-transparent\" : \"semi-transparent\";\n\t\t\t}\n\n\t\t\treturn \"opaque\";\n\t\t}\n\n\t\tif (cs.backgroundImage && cs.backgroundImage !== \"none\") {\n\t\t\treturn \"unknown\";\n\t\t}\n\n\t\treturn \"unknown\";\n\t};\n\n\tconst opacity = classifyOpacity(topCover);\n\tconst openingTag = topCover.cloneNode(false) as Element;\n\topeningTag.removeAttribute(obscurerAttr);\n\ttopCover.setAttribute(obscurerAttr, \"1\");\n\n\treturn {\n\t\tcoveredFraction: 1,\n\t\tfullyObscured: true,\n\t\toffscreen: false,\n\t\topacity,\n\t\tobscuredByHtml: openingTag.outerHTML,\n\t\thasObscurer: true,\n\t};\n}\n\n/** Return the element marked as the obscurer, if any. */\nexport function obscurerHandleScript(): Element | null {\n\treturn document.querySelector(\"[data-a11y-obscurer]\");\n}\n\nexport function clearObscurerScript(obscurerAttr: string): void {\n\tfor (const marked of Array.from(document.querySelectorAll(`[${obscurerAttr}]`))) {\n\t\tmarked.removeAttribute(obscurerAttr);\n\t}\n}\n\ntype ContextObserverState = {\n\tbaselineHref: string;\n\topenedWindow: boolean;\n\tsubmittedForm: boolean;\n\t/** Elements that received focusin since the last drain. */\n\tfocusIns: Element[];\n\tsoftUrlChange: boolean;\n\tinstalled: boolean;\n};\n\ndeclare global {\n\tinterface Window {\n\t\t__a11yContextObserver?: ContextObserverState;\n\t}\n}\n\n/**\n * Install once: wrap window.open (record + inert stub), capture-phase submit (record +\n * preventDefault), track focusin and soft URL changes.\n */\nexport function installContextObserverScript(): void {\n\tif (window.__a11yContextObserver?.installed) {\n\t\treturn;\n\t}\n\n\tconst state: ContextObserverState = {\n\t\tbaselineHref: location.href,\n\t\topenedWindow: false,\n\t\tsubmittedForm: false,\n\t\tfocusIns: [],\n\t\tsoftUrlChange: false,\n\t\tinstalled: true,\n\t};\n\n\twindow.__a11yContextObserver = state;\n\n\tconst originalOpen = window.open.bind(window);\n\n\twindow.open = (..._args: Parameters<typeof window.open>) => {\n\t\tstate.openedWindow = true;\n\n\t\treturn {\n\t\t\tclosed: true,\n\t\t\tclose() {},\n\t\t\tfocus() {},\n\t\t\tblur() {},\n\t\t\topener: null,\n\t\t\tlocation: { href: \"\" },\n\t\t} as unknown as Window;\n\t};\n\n\t// Keep a reference so tooling does not tree-shake the bind as unused.\n\tvoid originalOpen;\n\n\tdocument.addEventListener(\n\t\t\"submit\",\n\t\t(event) => {\n\t\t\tstate.submittedForm = true;\n\t\t\tevent.preventDefault();\n\t\t\tevent.stopPropagation();\n\t\t},\n\t\ttrue,\n\t);\n\n\tdocument.addEventListener(\n\t\t\"focus\",\n\t\t(event) => {\n\t\t\tconst target = event.target;\n\n\t\t\t// Prefer nodeType: instanceof can be brittle across some embeddings.\n\t\t\tif (target && (target as Node).nodeType === 1) {\n\t\t\t\tstate.focusIns.push(target as Element);\n\t\t\t}\n\t\t},\n\t\ttrue,\n\t);\n\n\tconst noteSoftNav = (): void => {\n\t\tif (location.href !== state.baselineHref) {\n\t\t\tstate.softUrlChange = true;\n\t\t}\n\t};\n\n\twindow.addEventListener(\"hashchange\", noteSoftNav);\n\twindow.addEventListener(\"popstate\", noteSoftNav);\n\n\tconst wrapHistory = (method: \"pushState\" | \"replaceState\"): void => {\n\t\tconst original = history[method].bind(history);\n\n\t\thistory[method] = ((...args: Parameters<History[\"pushState\"]>) => {\n\t\t\tconst result = original(...args);\n\t\t\tnoteSoftNav();\n\n\t\t\treturn result;\n\t\t}) as History[\"pushState\"];\n\t};\n\n\twrapHistory(\"pushState\");\n\twrapHistory(\"replaceState\");\n}\n\nexport type DrainContextObserverResult = {\n\topenedWindow: boolean;\n\tsubmittedForm: boolean;\n\tfocusRemoved: boolean;\n\tredirect: \"outside\" | \"same-subtree\" | null;\n\tsoftUrlChange: boolean;\n\tnavigation: boolean;\n\tattributedHtml: string | null;\n\t/** True when an attributed element was marked for selector resolution. */\n\thasAttributed: boolean;\n};\n\n/**\n * Drain observer signals for the current tab stop and reset per-stop flags. Classification of focus\n * removal / redirection uses the post-Tab settle activeElement and the focusin history from this\n * stop only.\n */\nexport function drainContextObserverScript(markerAttr: string): DrainContextObserverResult {\n\t// Must be a string literal inside this function: it is serialized into the page.\n\tconst attributedAttr = \"data-a11y-ctx-attr\";\n\n\tconst empty: DrainContextObserverResult = {\n\t\topenedWindow: false,\n\t\tsubmittedForm: false,\n\t\tfocusRemoved: false,\n\t\tredirect: null,\n\t\tsoftUrlChange: false,\n\t\tnavigation: false,\n\t\tattributedHtml: null,\n\t\thasAttributed: false,\n\t};\n\n\tconst state = window.__a11yContextObserver;\n\n\tif (!state) {\n\t\treturn empty;\n\t}\n\n\tfor (const marked of Array.from(document.querySelectorAll(`[${attributedAttr}]`))) {\n\t\tmarked.removeAttribute(attributedAttr);\n\t}\n\n\tconst openedWindow = state.openedWindow;\n\tconst submittedForm = state.submittedForm;\n\tconst softUrlChange = state.softUrlChange;\n\tconst focusIns = state.focusIns.slice();\n\n\tstate.openedWindow = false;\n\tstate.submittedForm = false;\n\tstate.softUrlChange = false;\n\tstate.focusIns = [];\n\n\tlet active = document.activeElement;\n\n\twhile (active?.shadowRoot?.activeElement) {\n\t\tactive = active.shadowRoot.activeElement;\n\t}\n\n\tconst intended =\n\t\tfocusIns.find((el) => el !== document.body && el !== document.documentElement) ?? null;\n\n\tconst openingHtml = (node: Element): string => {\n\t\tconst clone = node.cloneNode(false) as Element;\n\t\tclone.removeAttribute(markerAttr);\n\t\tclone.removeAttribute(attributedAttr);\n\n\t\treturn clone.outerHTML;\n\t};\n\n\tlet focusRemoved = false;\n\tlet redirect: \"outside\" | \"same-subtree\" | null = null;\n\tlet attributedHtml: string | null = null;\n\tlet hasAttributed = false;\n\n\tconst bodyOrNone = !active || active === document.body || active === document.documentElement;\n\n\tif (intended && bodyOrNone) {\n\t\tfocusRemoved = true;\n\t\tattributedHtml = openingHtml(intended);\n\t\tintended.setAttribute(attributedAttr, \"1\");\n\t\thasAttributed = true;\n\t} else if (intended && active && intended !== active) {\n\t\tconst composedContains = (ancestor: Element, node: Element): boolean => {\n\t\t\tlet current: Node | null = node;\n\n\t\t\twhile (current) {\n\t\t\t\tif (current === ancestor) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\n\t\t\t\tconst parent: Node | null = current.parentNode;\n\n\t\t\t\tif (parent) {\n\t\t\t\t\tcurrent = parent;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif (current instanceof ShadowRoot) {\n\t\t\t\t\tcurrent = current.host;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\treturn false;\n\t\t};\n\n\t\tconst same = composedContains(intended, active) || composedContains(active, intended);\n\n\t\tredirect = same ? \"same-subtree\" : \"outside\";\n\t\tattributedHtml = openingHtml(intended);\n\t\tintended.setAttribute(attributedAttr, \"1\");\n\t\thasAttributed = true;\n\t}\n\n\treturn {\n\t\topenedWindow,\n\t\tsubmittedForm,\n\t\tfocusRemoved,\n\t\tredirect,\n\t\tsoftUrlChange,\n\t\tnavigation: false,\n\t\tattributedHtml,\n\t\thasAttributed,\n\t};\n}\n\n/**\n * Return the element marked as attributed by drainContextObserverScript (the element that received\n * a focus event this stop and then lost it), if any.\n */\nexport function attributedHandleScript(): Element | null {\n\treturn document.querySelector(\"[data-a11y-ctx-attr]\");\n}\n\nexport function clearAttributedScript(): void {\n\tfor (const marked of Array.from(document.querySelectorAll(\"[data-a11y-ctx-attr]\"))) {\n\t\tmarked.removeAttribute(\"data-a11y-ctx-attr\");\n\t}\n}\n\n/**\n * Drop focusin history accumulated after drain (e.g. pixel-diff blur/refocus) so the next tab stop\n * does not mis-attribute F55 or redirects.\n */\nexport function clearContextFocusInsScript(): void {\n\tconst state = window.__a11yContextObserver;\n\n\tif (state) {\n\t\tstate.focusIns = [];\n\t\tstate.openedWindow = false;\n\t\tstate.submittedForm = false;\n\t\tstate.softUrlChange = false;\n\t}\n}\n\n/** Current location.href for navigation comparison in the host. */\nexport function locationHrefScript(): string {\n\treturn location.href;\n}\n\nexport function hasFocusScript(): boolean {\n\treturn document.hasFocus();\n}\n","/**\n * Computed-style properties that are likely to draw a visible focus indicator. Layout properties\n * like margin and padding are deliberately excluded.\n */\nexport const FOCUS_STYLE_PROPERTIES = [\n\t\"background-color\",\n\t\"background-image\",\n\t\"border-bottom-color\",\n\t\"border-bottom-style\",\n\t\"border-bottom-width\",\n\t\"border-left-color\",\n\t\"border-left-style\",\n\t\"border-left-width\",\n\t\"border-right-color\",\n\t\"border-right-style\",\n\t\"border-right-width\",\n\t\"border-top-color\",\n\t\"border-top-style\",\n\t\"border-top-width\",\n\t\"box-shadow\",\n\t\"color\",\n\t\"font-weight\",\n\t\"outline-color\",\n\t\"outline-style\",\n\t\"outline-width\",\n\t\"text-decoration\",\n] as const;\n","import type { BrowserAdaptor, ElementRef } from \"@a11y-pulse/browser-adaptor\";\nimport { getSelector, truncateHtml } from \"@a11y-pulse/browser-adaptor/dom\";\nimport {\n\ttype ActiveElementBase,\n\tactiveElementHandleScript,\n\tattributedHandleScript,\n\tbaselineScript,\n\tclearAttributedScript,\n\tclearContextFocusInsScript,\n\tclearMarkersScript,\n\tclearObscurerScript,\n\ttype DrainContextObserverResult,\n\tdrainContextObserverScript,\n\thasFocusScript,\n\theldFocusScript,\n\tinstallContextObserverScript,\n\tinstallTabObserverScript,\n\tlocationHrefScript,\n\tmarkActiveElementScript,\n\tmeasureObscuringScript,\n\tobscurerHandleScript,\n\tprobeActiveElementScript,\n\tremoveTabObserverScript,\n\tTABBABLE_SELECTOR,\n} from \"./browser-scripts\";\nimport { captureScreenshot } from \"./capture-screenshot\";\nimport { FOCUS_STYLE_PROPERTIES } from \"./focus-style\";\nimport type {\n\tActiveElementInfo,\n\tCapability,\n\tContextChangeSignals,\n\tSessionEndReason,\n\tStyleSnapshot,\n\tTabConsumer,\n\tTabSessionHandle,\n\tTabStopSnapshot,\n\tUnfocusedPair,\n} from \"./types\";\nimport { captureUnfocusedPair } from \"./unfocused-pair\";\n\n/**\n * Whether an `evaluate()` rejection looks like the page navigated out from\n * under it (execution context destroyed) rather than an unexpected failure.\n * A full navigation is the one context-change signal the in-page observer\n * can never self-report: it destroys the JS context before it can log\n * anything, so the host has to infer it from how `evaluate()` failed.\n */\nexport function isContextDestroyedError(error: unknown): boolean {\n\tconst message = error instanceof Error ? error.message : String(error);\n\n\t// Each adaptor's driver words this differently: \"Target closed\" is Puppeteer's, \"has been\n\t// closed\" is Playwright's.\n\treturn /Execution context was destroyed|Target closed|has been closed|frame was detached|navigat/i.test(\n\t\tmessage,\n\t);\n}\n\n/**\n * Outcome of the early, pre-isBody navigation check. `navigation: true`\n * means a full navigation was detected (via href diff or a destroyed-context\n * error) and the caller must end the session without proceeding any\n * further. Otherwise `raw` carries a drain payload already fetched while\n * resolving a soft URL change (so the later full drain can reuse it instead\n * of reading back state the first call already reset), or `null` when the\n * href was unchanged and nothing has been drained yet for this stop.\n */\ntype ContextNavigationOutcome =\n\t| { navigation: true }\n\t| { navigation: false; raw: DrainContextObserverResult | null };\n\nexport const DEFAULT_SCREENSHOT_SETTLE_DELAY = 33;\nexport const DEFAULT_SCREENSHOT_CLIP_BUFFER = 10;\nexport const DEFAULT_MARKER_LIMIT = 1024;\nexport const MARKER_ATTR = \"data-a11y-focus-idx\";\nexport const OBSCURER_ATTR = \"data-a11y-obscurer\";\nexport const OBSCURING_RECHECK_DELAY_MS = 250;\n\n// Consecutive Tab presses one element may absorb before it counts as stuck.\nconst MAX_INTERNAL_STOPS = 10;\n\n// Consecutive Tab presses focus may spend inside iframes, whose own elements the loop cannot tell\n// apart, before it counts as stuck there.\nconst MAX_IFRAME_STOPS = 256;\n\nexport type TabSessionOptions = {\n\tscreenshotSettleDelay?: number;\n\tscreenshotClipBuffer?: number;\n\tmarkerLimit?: number;\n\tbaselineElementLimit?: number;\n};\n\nexport type TabOrchestrator = {\n\tattach(consumer: TabConsumer): void;\n\trun(): Promise<void>;\n};\n\nexport function createTabOrchestrator(\n\tadaptor: BrowserAdaptor,\n\toptions: TabSessionOptions = {},\n): TabOrchestrator {\n\tconst consumers: TabConsumer[] = [];\n\tlet started = false;\n\tlet ran = false;\n\n\treturn {\n\t\tattach(consumer) {\n\t\t\tif (started) {\n\t\t\t\tthrow new Error(\"Cannot attach after run() has started\");\n\t\t\t}\n\n\t\t\tconsumers.push(consumer);\n\t\t},\n\t\tasync run() {\n\t\t\tif (ran) {\n\t\t\t\tthrow new Error(\"run() has already been called\");\n\t\t\t}\n\n\t\t\tran = true;\n\t\t\tstarted = true;\n\n\t\t\tif (consumers.length === 0) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst attached = new Set(consumers);\n\t\t\tconst screenshotSettleDelay =\n\t\t\t\toptions.screenshotSettleDelay ?? DEFAULT_SCREENSHOT_SETTLE_DELAY;\n\t\t\tconst screenshotClipBuffer = options.screenshotClipBuffer ?? DEFAULT_SCREENSHOT_CLIP_BUFFER;\n\t\t\tconst markerLimit = Math.max(\n\t\t\t\toptions.markerLimit ?? DEFAULT_MARKER_LIMIT,\n\t\t\t\toptions.baselineElementLimit ?? 0,\n\t\t\t);\n\t\t\tconst styleProps = [...FOCUS_STYLE_PROPERTIES];\n\t\t\tconst resolved = {\n\t\t\t\tscreenshotSettleDelay,\n\t\t\t\tscreenshotClipBuffer,\n\t\t\t\tstyleProps,\n\t\t\t};\n\n\t\t\tlet settleTimer: ReturnType<typeof setTimeout> | undefined;\n\t\t\tlet resolveSettle: (() => void) | undefined;\n\t\t\tconst pairByStop = { current: null as Promise<UnfocusedPair> | null };\n\t\t\tconst screenshotByStop = { current: null as Promise<Uint8Array> | null };\n\t\t\tlet activeHandle: ElementRef | undefined;\n\t\t\tlet lastHasAttributed = false;\n\t\t\t// Captured when observers are installed and re-armed after every\n\t\t\t// detected soft URL change, so repeated soft navigations across stops\n\t\t\t// compare against the most recent href rather than the original load.\n\t\t\tlet baselineHref: string | null = null;\n\n\t\t\tconst remainingHas = (capability: Capability): boolean =>\n\t\t\t\t[...attached].some((c) => c.capabilities.has(capability));\n\n\t\t\t// Decided once from who is attached at session start; never re-checked,\n\t\t\t// so the observer stays installed even after every declarer disconnects\n\t\t\t// (session-lifetime setup is never uninstalled, only skipped per-stop).\n\t\t\tconst contextSignalsWanted = remainingHas(\"contextSignals\");\n\n\t\t\tconst handleFor = (consumer: TabConsumer): TabSessionHandle => ({\n\t\t\t\tdisconnect() {\n\t\t\t\t\tattached.delete(consumer);\n\n\t\t\t\t\tif (attached.size === 0 && settleTimer !== undefined) {\n\t\t\t\t\t\tclearTimeout(settleTimer);\n\t\t\t\t\t\tsettleTimer = undefined;\n\t\t\t\t\t\tresolveSettle?.();\n\t\t\t\t\t\tresolveSettle = undefined;\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tensureUnfocusedPair: () => {\n\t\t\t\t\tif (!consumer.capabilities.has(\"unfocusedPair\")) {\n\t\t\t\t\t\tthrow new Error(\"Consumer did not declare unfocusedPair\");\n\t\t\t\t\t}\n\n\t\t\t\t\tif (!remainingHas(\"unfocusedPair\")) {\n\t\t\t\t\t\tthrow new Error(\"Consumer did not declare unfocusedPair\");\n\t\t\t\t\t}\n\n\t\t\t\t\tif (activeHandle === undefined) {\n\t\t\t\t\t\tthrow new Error(\"unfocusedPair capture not implemented\");\n\t\t\t\t\t}\n\n\t\t\t\t\tpairByStop.current ??= captureUnfocusedPair(adaptor, resolved, activeHandle);\n\n\t\t\t\t\treturn pairByStop.current;\n\t\t\t\t},\n\t\t\t\tscreenshotClip: () => {\n\t\t\t\t\tif (!consumer.capabilities.has(\"screenshot\")) {\n\t\t\t\t\t\tthrow new Error(\"Consumer did not declare screenshot\");\n\t\t\t\t\t}\n\n\t\t\t\t\tif (!remainingHas(\"screenshot\")) {\n\t\t\t\t\t\tthrow new Error(\"Consumer did not declare screenshot\");\n\t\t\t\t\t}\n\n\t\t\t\t\tif (activeHandle === undefined) {\n\t\t\t\t\t\tthrow new Error(\"screenshot capture not implemented\");\n\t\t\t\t\t}\n\n\t\t\t\t\tscreenshotByStop.current ??= captureScreenshot(adaptor, resolved, activeHandle);\n\n\t\t\t\t\treturn screenshotByStop.current;\n\t\t\t\t},\n\t\t\t});\n\n\t\t\tconst end = (reason: SessionEndReason, revisitedIndex: number | null = null): void => {\n\t\t\t\tfor (const consumer of attached) {\n\t\t\t\t\tconsumer.onSessionEnd?.(reason, { revisitedIndex });\n\t\t\t\t}\n\n\t\t\t\tattached.clear();\n\t\t\t};\n\n\t\t\t/**\n\t\t\t * Primary navigation detection layer: compares `location.href` against\n\t\t\t * `baselineHref` on every stop, since a full navigation destroys the\n\t\t\t * in-page observer's execution context before it can self-report\n\t\t\t * anything (`drainContextObserverScript`'s own `navigation` field is\n\t\t\t * always `false`). A destroyed context can also simply throw instead\n\t\t\t * of returning a changed href (e.g. the navigation completes between\n\t\t\t * the href read and the drain read), which is treated the same way.\n\t\t\t *\n\t\t\t * Must run before the caller's `isBody`/\"done tabbing\" exit check: a\n\t\t\t * freshly-navigated page's `document.activeElement` defaults to\n\t\t\t * `document.body` unless the new page autofocuses something, so\n\t\t\t * without this proactive check a silent navigation would otherwise be\n\t\t\t * misclassified as `\"completed\"`.\n\t\t\t *\n\t\t\t * Deliberately cheap on the common (non-navigating) path: it only\n\t\t\t * reads `location.href` and does not call `drainContextObserverScript`\n\t\t\t * unless the href actually changed, so a stop that ends the session\n\t\t\t * via the isBody exit right after this check still drains at most\n\t\t\t * once overall (at the later, full-mapping call site).\n\t\t\t */\n\t\t\tasync function checkContextNavigation(): Promise<ContextNavigationOutcome> {\n\t\t\t\tlet href: string;\n\n\t\t\t\ttry {\n\t\t\t\t\thref = await adaptor.evaluate(locationHrefScript);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (!isContextDestroyedError(error)) {\n\t\t\t\t\t\tthrow error;\n\t\t\t\t\t}\n\n\t\t\t\t\treturn { navigation: true };\n\t\t\t\t}\n\n\t\t\t\tif (baselineHref === null || href === baselineHref) {\n\t\t\t\t\treturn { navigation: false, raw: null };\n\t\t\t\t}\n\n\t\t\t\t// Document navigated (or soft URL change). Prefer the observer's\n\t\t\t\t// own soft-nav flag when the execution context survived;\n\t\t\t\t// otherwise treat it as a full navigation.\n\t\t\t\tlet raw: DrainContextObserverResult | null = null;\n\n\t\t\t\ttry {\n\t\t\t\t\traw = await adaptor.evaluate(drainContextObserverScript, MARKER_ATTR);\n\t\t\t\t} catch {\n\t\t\t\t\traw = null;\n\t\t\t\t}\n\n\t\t\t\tif (raw === null || !raw.softUrlChange) {\n\t\t\t\t\treturn { navigation: true };\n\t\t\t\t}\n\n\t\t\t\tbaselineHref = href;\n\n\t\t\t\treturn { navigation: false, raw };\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Whether the page reports focus, re-asserting focus reporting once\n\t\t\t * before answering no.\n\t\t\t */\n\t\t\tasync function documentHasFocus(): Promise<boolean> {\n\t\t\t\tif (await adaptor.evaluate(hasFocusScript)) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\n\t\t\t\t// Another target taking the foreground clears focus emulation.\n\t\t\t\tawait adaptor.ensureFocusReporting();\n\n\t\t\t\treturn adaptor.evaluate(hasFocusScript);\n\t\t\t}\n\n\t\t\tlet failure: unknown;\n\n\t\t\ttry {\n\t\t\t\tawait adaptor.ensureFocusReporting();\n\n\t\t\t\tconst payload = await adaptor.evaluate(\n\t\t\t\t\tbaselineScript,\n\t\t\t\t\tstyleProps,\n\t\t\t\t\tMARKER_ATTR,\n\t\t\t\t\tmarkerLimit,\n\t\t\t\t);\n\t\t\t\tawait adaptor.evaluate(installTabObserverScript);\n\t\t\t\tconst interned = new Map<number, StyleSnapshot>();\n\n\t\t\t\tfor (const entry of payload.entries) {\n\t\t\t\t\tconst styles = payload.styles[entry.styleIndex];\n\n\t\t\t\t\tif (styles !== undefined) {\n\t\t\t\t\t\tinterned.set(entry.index, styles);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (contextSignalsWanted) {\n\t\t\t\t\tbaselineHref = await adaptor.evaluate(locationHrefScript);\n\t\t\t\t\tawait adaptor.evaluate(installContextObserverScript);\n\t\t\t\t}\n\n\t\t\t\tfor (const consumer of [...attached]) {\n\t\t\t\t\tawait consumer.onSessionStart?.(handleFor(consumer));\n\t\t\t\t}\n\n\t\t\t\tconst visited = new Set<number>();\n\t\t\t\tlet tabIndex = 0;\n\t\t\t\t// Elements the baseline did not mark (media players, summaries, content added later) are\n\t\t\t\t// marked when first focused, numbered from the limit no baseline index can reach.\n\t\t\t\tlet nextMarker = markerLimit;\n\t\t\t\tlet previousIndex: number | null = null;\n\t\t\t\tlet internalStops = 0;\n\t\t\t\tlet iframeStops = 0;\n\n\t\t\t\twhile (attached.size > 0) {\n\t\t\t\t\tpairByStop.current = null;\n\t\t\t\t\tscreenshotByStop.current = null;\n\t\t\t\t\tlastHasAttributed = false;\n\n\t\t\t\t\tif (!(await documentHasFocus())) {\n\t\t\t\t\t\tend(\"lostFocus\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tawait adaptor.pressTab();\n\t\t\t\t\tawait new Promise<void>((resolve) => {\n\t\t\t\t\t\tresolveSettle = resolve;\n\t\t\t\t\t\tsettleTimer = setTimeout(() => {\n\t\t\t\t\t\t\tsettleTimer = undefined;\n\t\t\t\t\t\t\tresolveSettle = undefined;\n\t\t\t\t\t\t\tresolve();\n\t\t\t\t\t\t}, screenshotSettleDelay);\n\t\t\t\t\t});\n\n\t\t\t\t\tif (attached.size === 0) {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Chromium only matches :focus while the document reports focus, so\n\t\t\t\t\t// an element measured after a loss reads as having no indicator\n\t\t\t\t\t// however correct its CSS is. Read here, at the point of\n\t\t\t\t\t// measurement, rather than relying on the pre-tab check. Acted on\n\t\t\t\t\t// below, once the probe has said whether there was anything to\n\t\t\t\t\t// measure.\n\t\t\t\t\tconst focusedAtMeasurement = await documentHasFocus();\n\n\t\t\t\t\tlet base: ActiveElementBase | null;\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tbase = await adaptor.evaluate(probeActiveElementScript, styleProps, MARKER_ATTR);\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tif (remainingHas(\"contextSignals\") && isContextDestroyedError(error)) {\n\t\t\t\t\t\t\t// Mid-flight navigation destroyed the execution context\n\t\t\t\t\t\t\t// before the probe could even run: there is no active\n\t\t\t\t\t\t\t// element to report, so end directly (mirrors the\n\t\t\t\t\t\t\t// isBody/\"done tabbing\" exit below, which also ends\n\t\t\t\t\t\t\t// without a snapshot when there is nothing to notify).\n\t\t\t\t\t\t\tend(\"navigation\");\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tthrow error;\n\t\t\t\t\t}\n\n\t\t\t\t\t// The press that leaves the last element takes focus out of the\n\t\t\t\t\t// document with it, so an unfocused page probing as <body> is the\n\t\t\t\t\t// tab order running out, not a stolen focus. Re-asserting focus\n\t\t\t\t\t// emulation cannot bring that back either. Leave it to the isBody\n\t\t\t\t\t// exit below and treat only a real element measured without focus\n\t\t\t\t\t// as a genuine loss.\n\t\t\t\t\tif (!focusedAtMeasurement && base !== null && !base.isBody) {\n\t\t\t\t\t\tend(\"lostFocus\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Primary navigation detection layer, run before the isBody exit:\n\t\t\t\t\t// a silently-navigated page's evaluate() calls succeed against the\n\t\t\t\t\t// new document (Puppeteer/CDP targets whichever context is live at\n\t\t\t\t\t// call time), so a hard nav can surface as an ordinary `isBody: true`\n\t\t\t\t\t// probe result with no thrown error at all.\n\t\t\t\t\tlet contextNav: ContextNavigationOutcome | null = null;\n\n\t\t\t\t\tif (remainingHas(\"contextSignals\")) {\n\t\t\t\t\t\tconst outcome = await checkContextNavigation();\n\n\t\t\t\t\t\tif (outcome.navigation) {\n\t\t\t\t\t\t\tend(\"navigation\");\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tcontextNav = outcome;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (base === null || base.isBody) {\n\t\t\t\t\t\t// Focus landed on <body>. This is normally \"done tabbing\", but it\n\t\t\t\t\t\t// is also what F55 focus-removal looks like (an element receives\n\t\t\t\t\t\t// focus and then blurs itself, e.g. `onfocus=\"this.blur()\"`): the\n\t\t\t\t\t\t// in-page observer's focusin history still attributes that stop to\n\t\t\t\t\t\t// the element that briefly held focus, even though nothing is\n\t\t\t\t\t\t// focused now. Drain before ending so that case gets one last\n\t\t\t\t\t\t// notification instead of vanishing silently.\n\t\t\t\t\t\tif (remainingHas(\"contextSignals\")) {\n\t\t\t\t\t\t\tlet raw: DrainContextObserverResult;\n\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\traw =\n\t\t\t\t\t\t\t\t\tcontextNav?.raw ??\n\t\t\t\t\t\t\t\t\t(await adaptor.evaluate(drainContextObserverScript, MARKER_ATTR));\n\t\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\t\tif (!isContextDestroyedError(error)) {\n\t\t\t\t\t\t\t\t\tthrow error;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// Mirrors the probe's own destroyed-context catch above:\n\t\t\t\t\t\t\t\t// there is no attributed element left to report, so end\n\t\t\t\t\t\t\t\t// directly with \"navigation\" rather than \"completed\" (a\n\t\t\t\t\t\t\t\t// navigation signal always ends the session that way).\n\t\t\t\t\t\t\t\tend(\"navigation\");\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif (raw.hasAttributed) {\n\t\t\t\t\t\t\t\tconst attributedRef = await adaptor.evaluateHandle(attributedHandleScript);\n\t\t\t\t\t\t\t\tlet selector: string;\n\n\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\tselector = await adaptor.evaluate(getSelector, attributedRef);\n\t\t\t\t\t\t\t\t} finally {\n\t\t\t\t\t\t\t\t\tawait adaptor.disposeRef(attributedRef);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tconst activeElement: ActiveElementInfo = {\n\t\t\t\t\t\t\t\t\tindex: null,\n\t\t\t\t\t\t\t\t\tisBody: false,\n\t\t\t\t\t\t\t\t\tisIframe: false,\n\t\t\t\t\t\t\t\t\tselector,\n\t\t\t\t\t\t\t\t\thtml: truncateHtml(raw.attributedHtml ?? \"\"),\n\t\t\t\t\t\t\t\t\tstyles: { element: {}, before: {}, after: {} },\n\t\t\t\t\t\t\t\t\trect: { x: 0, y: 0, width: 0, height: 0 },\n\t\t\t\t\t\t\t\t};\n\n\t\t\t\t\t\t\t\tconst snapshot: TabStopSnapshot = {\n\t\t\t\t\t\t\t\t\ttabIndex: ++tabIndex,\n\t\t\t\t\t\t\t\t\tactiveElement,\n\t\t\t\t\t\t\t\t\tcontextSignals: {\n\t\t\t\t\t\t\t\t\t\tsignals: {\n\t\t\t\t\t\t\t\t\t\t\topenedWindow: raw.openedWindow,\n\t\t\t\t\t\t\t\t\t\t\tsubmittedForm: raw.submittedForm,\n\t\t\t\t\t\t\t\t\t\t\tfocusRemoved: raw.focusRemoved,\n\t\t\t\t\t\t\t\t\t\t\tredirect: raw.redirect,\n\t\t\t\t\t\t\t\t\t\t\tsoftUrlChange: raw.softUrlChange,\n\t\t\t\t\t\t\t\t\t\t\tnavigation: raw.navigation,\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t};\n\n\t\t\t\t\t\t\t\t// This synthetic stop has no live focused element (it\n\t\t\t\t\t\t\t\t// already blurred itself before this notification is\n\t\t\t\t\t\t\t\t// built, by definition of F55), so capabilities that\n\t\t\t\t\t\t\t\t// need one (`unfocusedPair`, `baselineStyles`,\n\t\t\t\t\t\t\t\t// `obscuring`) have nothing to meaningfully act on.\n\t\t\t\t\t\t\t\t// Only consumers that declared `contextSignals` (the\n\t\t\t\t\t\t\t\t// capability this whole synthetic-stop mechanism exists\n\t\t\t\t\t\t\t\t// to serve) are notified; unlike every other stop in\n\t\t\t\t\t\t\t\t// this file, uniform notification is wrong here because\n\t\t\t\t\t\t\t\t// there is no element for the other capabilities to\n\t\t\t\t\t\t\t\t// read from.\n\t\t\t\t\t\t\t\tconst recipients = [...attached].filter((consumer) =>\n\t\t\t\t\t\t\t\t\tconsumer.capabilities.has(\"contextSignals\"),\n\t\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\tfor (const consumer of recipients) {\n\t\t\t\t\t\t\t\t\t\tawait consumer.onTabStop(snapshot, handleFor(consumer));\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t} finally {\n\t\t\t\t\t\t\t\t\t// Guaranteed even if a recipient's onTabStop throws,\n\t\t\t\t\t\t\t\t\t// so the `data-a11y-ctx-attr` DOM marker never survives\n\t\t\t\t\t\t\t\t\t// past this stop (mirrors how `disposeRef` is\n\t\t\t\t\t\t\t\t\t// guaranteed via `finally` in the main per-stop loop\n\t\t\t\t\t\t\t\t\t// below).\n\t\t\t\t\t\t\t\t\tawait adaptor.evaluate(clearAttributedScript);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tend(\"completed\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (base.isIframe) {\n\t\t\t\t\t\tpreviousIndex = null;\n\n\t\t\t\t\t\tif (++iframeStops > MAX_IFRAME_STOPS) {\n\t\t\t\t\t\t\tend(\"stuck\");\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tiframeStops = 0;\n\n\t\t\t\t\tif (base.index === null) {\n\t\t\t\t\t\tbase.index = await adaptor.evaluate(markActiveElementScript, MARKER_ATTR, nextMarker);\n\t\t\t\t\t\tnextMarker = Math.max(nextMarker, base.index + 1);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (base.index === previousIndex) {\n\t\t\t\t\t\tconst held = await adaptor.evaluate(heldFocusScript, TABBABLE_SELECTOR);\n\n\t\t\t\t\t\t// Focus going nowhere with nothing after it is the end of the tab order in\n\t\t\t\t\t\t// browsers that keep focus on the last element rather than leaving the page.\n\t\t\t\t\t\tif (!held.prevented && !held.followed) {\n\t\t\t\t\t\t\tend(\"completed\");\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// The page holding on to focus is a cycle of one. Otherwise focus is moving between\n\t\t\t\t\t\t// a control's own parts (media player buttons, the fields of a date input), unless\n\t\t\t\t\t\t// it never comes out.\n\t\t\t\t\t\tif (held.prevented || ++internalStops > MAX_INTERNAL_STOPS) {\n\t\t\t\t\t\t\tend(\"completed\", base.index);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tpreviousIndex = base.index;\n\t\t\t\t\tinternalStops = 0;\n\n\t\t\t\t\tif (visited.has(base.index)) {\n\t\t\t\t\t\tend(\"completed\", base.index);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tvisited.add(base.index);\n\n\t\t\t\t\tconst ref = await adaptor.evaluateHandle(activeElementHandleScript);\n\t\t\t\t\tactiveHandle = ref;\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst selector = await adaptor.evaluate(getSelector, ref);\n\n\t\t\t\t\t\tconst activeElement: ActiveElementInfo = {\n\t\t\t\t\t\t\t...base,\n\t\t\t\t\t\t\thtml: truncateHtml(base.html),\n\t\t\t\t\t\t\tselector,\n\t\t\t\t\t\t};\n\n\t\t\t\t\t\tconst snapshot: TabStopSnapshot = {\n\t\t\t\t\t\t\ttabIndex: ++tabIndex,\n\t\t\t\t\t\t\tactiveElement,\n\t\t\t\t\t\t};\n\n\t\t\t\t\t\tif (remainingHas(\"baselineStyles\") && activeElement.index !== null) {\n\t\t\t\t\t\t\tconst baselineStyles = interned.get(activeElement.index);\n\n\t\t\t\t\t\t\tif (baselineStyles !== undefined) {\n\t\t\t\t\t\t\t\tsnapshot.baselineStyles = baselineStyles;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (remainingHas(\"contextSignals\")) {\n\t\t\t\t\t\t\tlet signals: ContextChangeSignals;\n\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t// Reuse the drain already fetched above while resolving\n\t\t\t\t\t\t\t\t// a soft URL change (checkContextNavigation), if any;\n\t\t\t\t\t\t\t\t// re-draining here would read back state that call\n\t\t\t\t\t\t\t\t// already reset (focusIns, softUrlChange, etc.) and\n\t\t\t\t\t\t\t\t// silently lose whatever it was reporting. Otherwise\n\t\t\t\t\t\t\t\t// (the common case: href unchanged, nothing drained\n\t\t\t\t\t\t\t\t// yet) do the one drain this stop needs.\n\t\t\t\t\t\t\t\tconst raw =\n\t\t\t\t\t\t\t\t\tcontextNav?.raw ??\n\t\t\t\t\t\t\t\t\t(await adaptor.evaluate(drainContextObserverScript, MARKER_ATTR));\n\t\t\t\t\t\t\t\tsignals = {\n\t\t\t\t\t\t\t\t\topenedWindow: raw.openedWindow,\n\t\t\t\t\t\t\t\t\tsubmittedForm: raw.submittedForm,\n\t\t\t\t\t\t\t\t\tfocusRemoved: raw.focusRemoved,\n\t\t\t\t\t\t\t\t\tredirect: raw.redirect,\n\t\t\t\t\t\t\t\t\tsoftUrlChange: raw.softUrlChange,\n\t\t\t\t\t\t\t\t\tnavigation: raw.navigation,\n\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\tlastHasAttributed = raw.hasAttributed;\n\t\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\t\tif (!isContextDestroyedError(error)) {\n\t\t\t\t\t\t\t\t\tthrow error;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// The page navigated out from under the drain call: the\n\t\t\t\t\t\t\t\t// in-page observer's own state (and any DOM markers it\n\t\t\t\t\t\t\t\t// set) went with it, so there is nothing left to report\n\t\t\t\t\t\t\t\t// besides the navigation itself.\n\t\t\t\t\t\t\t\tsignals = {\n\t\t\t\t\t\t\t\t\topenedWindow: false,\n\t\t\t\t\t\t\t\t\tsubmittedForm: false,\n\t\t\t\t\t\t\t\t\tfocusRemoved: false,\n\t\t\t\t\t\t\t\t\tredirect: null,\n\t\t\t\t\t\t\t\t\tsoftUrlChange: false,\n\t\t\t\t\t\t\t\t\tnavigation: true,\n\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\tlastHasAttributed = false;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tsnapshot.contextSignals = { signals };\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (remainingHas(\"obscuring\")) {\n\t\t\t\t\t\t\tlet raw = await adaptor.evaluate(measureObscuringScript, ref, OBSCURER_ATTR);\n\n\t\t\t\t\t\t\tif (raw.fullyObscured) {\n\t\t\t\t\t\t\t\tawait new Promise((resolve) => setTimeout(resolve, OBSCURING_RECHECK_DELAY_MS));\n\t\t\t\t\t\t\t\traw = await adaptor.evaluate(measureObscuringScript, ref, OBSCURER_ATTR);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tlet obscuredBy: { selector: string; html: string } | null = null;\n\n\t\t\t\t\t\t\tif (raw.hasObscurer) {\n\t\t\t\t\t\t\t\tconst obscurer = await adaptor.evaluateHandle(obscurerHandleScript);\n\n\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\tconst obscurerSelector = await adaptor.evaluate(getSelector, obscurer);\n\t\t\t\t\t\t\t\t\tobscuredBy = {\n\t\t\t\t\t\t\t\t\t\tselector: obscurerSelector,\n\t\t\t\t\t\t\t\t\t\thtml: truncateHtml(raw.obscuredByHtml ?? \"\"),\n\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t} finally {\n\t\t\t\t\t\t\t\t\tawait adaptor.disposeRef(obscurer);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tsnapshot.obscuring = {\n\t\t\t\t\t\t\t\tcoveredFraction: raw.coveredFraction,\n\t\t\t\t\t\t\t\tfullyObscured: raw.fullyObscured,\n\t\t\t\t\t\t\t\toffscreen: raw.offscreen,\n\t\t\t\t\t\t\t\topacity: raw.opacity,\n\t\t\t\t\t\t\t\tobscuredBy,\n\t\t\t\t\t\t\t};\n\n\t\t\t\t\t\t\tawait adaptor.evaluate(clearObscurerScript, OBSCURER_ATTR);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst recipients = [...attached];\n\n\t\t\t\t\t\tfor (const consumer of recipients) {\n\t\t\t\t\t\t\tawait consumer.onTabStop(snapshot, handleFor(consumer));\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (snapshot.contextSignals?.signals.navigation) {\n\t\t\t\t\t\t\tend(\"navigation\");\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t} finally {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tif (pairByStop.current !== null) {\n\t\t\t\t\t\t\t\tawait pairByStop.current;\n\n\t\t\t\t\t\t\t\tif (contextSignalsWanted) {\n\t\t\t\t\t\t\t\t\t// ensureUnfocusedPair blurred and re-focused the element to\n\t\t\t\t\t\t\t\t\t// capture the unfocused screenshot; drop that noise so the\n\t\t\t\t\t\t\t\t\t// next drain doesn't misread it as a real focus-removal or\n\t\t\t\t\t\t\t\t\t// redirect.\n\t\t\t\t\t\t\t\t\tawait adaptor.evaluate(clearContextFocusInsScript);\n\n\t\t\t\t\t\t\t\t\tif (lastHasAttributed) {\n\t\t\t\t\t\t\t\t\t\tawait adaptor.evaluate(clearAttributedScript);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t// Capture errors already surface through the consumer that\n\t\t\t\t\t\t\t// awaited; still dispose the handle below.\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tactiveHandle = undefined;\n\t\t\t\t\t\tawait adaptor.disposeRef(ref);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tfailure = error;\n\t\t\t} finally {\n\t\t\t\t// Each teardown step is isolated so one throwing cannot skip the other or replace the\n\t\t\t\t// loop error.\n\t\t\t\ttry {\n\t\t\t\t\tawait adaptor.evaluate(clearMarkersScript, MARKER_ATTR);\n\t\t\t\t} catch {}\n\n\t\t\t\ttry {\n\t\t\t\t\tawait adaptor.evaluate(removeTabObserverScript);\n\t\t\t\t} catch {}\n\t\t\t}\n\n\t\t\tif (failure !== undefined) {\n\t\t\t\tend(\"failed\");\n\t\t\t\tthrow failure;\n\t\t\t}\n\t\t},\n\t};\n}\n","import type { BrowserAdaptor, ElementRef } from \"@a11y-pulse/browser-adaptor\";\nimport { bufferedClip } from \"@a11y-pulse/browser-adaptor\";\nimport { elementRectScript, pageDimensionsScript } from \"./browser-scripts\";\n\nexport type CaptureScreenshotOptions = {\n\tscreenshotClipBuffer: number;\n};\n\n/**\n * Screenshot the element `handle` currently refers to, clipped to its box plus\n * `screenshotClipBuffer` padding. The caller owns `handle` and must dispose it after this promise\n * settles.\n */\nexport async function captureScreenshot(\n\tadaptor: BrowserAdaptor,\n\toptions: CaptureScreenshotOptions,\n\thandle: ElementRef,\n): Promise<Uint8Array> {\n\tconst { width: pageWidth, height: pageHeight } = await adaptor.evaluate(pageDimensionsScript);\n\tconst rect = await adaptor.evaluate(elementRectScript, handle);\n\tconst clip = bufferedClip(rect, pageWidth, pageHeight, options.screenshotClipBuffer);\n\tconst scale = adaptor.screenshotClipScale ?? 1;\n\n\treturn adaptor.screenshotClip(clip, scale);\n}\n","import type { BrowserAdaptor, ElementRef } from \"@a11y-pulse/browser-adaptor\";\nimport { bufferedClip } from \"@a11y-pulse/browser-adaptor\";\nimport {\n\tblurScript,\n\telementRectScript,\n\telementStylesScript,\n\tfocusScript,\n\tneedsCentringScript,\n\tpageDimensionsScript,\n\tscrollToCenterScript,\n} from \"./browser-scripts\";\nimport type { UnfocusedPair } from \"./types\";\n\nexport type CaptureUnfocusedPairOptions = {\n\tscreenshotSettleDelay: number;\n\tscreenshotClipBuffer: number;\n\tstyleProps: readonly string[];\n};\n\n/**\n * Capture focused and unfocused screenshots of the same element, plus the unfocused style snapshot\n * and clip anchors appearance needs to score later.\n *\n * The caller owns `handle` and must dispose it after this promise settles.\n */\nexport async function captureUnfocusedPair(\n\tadaptor: BrowserAdaptor,\n\toptions: CaptureUnfocusedPairOptions,\n\thandle: ElementRef,\n): Promise<UnfocusedPair> {\n\t// Tab scrolls an element just barely into view at the viewport edge, which is exactly where fixed\n\t// overlays (cookie banners, sticky footers) sit. If something covers the element there, both\n\t// screenshots would show the overlay and no indicator could ever be detected. The clip's padding\n\t// also hangs past the edge there, and a screenshot only captures what is inside the viewport, so\n\t// an indicator drawn in that padding would be lost. Centre the element in the viewport first in\n\t// either case, then give the scroll a moment to settle.\n\tif (await adaptor.evaluate(needsCentringScript, handle, options.screenshotClipBuffer)) {\n\t\tawait adaptor.evaluate(scrollToCenterScript, handle, options.screenshotClipBuffer);\n\n\t\tawait new Promise((resolve) => setTimeout(resolve, options.screenshotSettleDelay));\n\t}\n\n\tconst { width: pageWidth, height: pageHeight } = await adaptor.evaluate(pageDimensionsScript);\n\n\tconst focusedRect = await adaptor.evaluate(elementRectScript, handle);\n\tconst focusedClip = bufferedClip(\n\t\tfocusedRect,\n\t\tpageWidth,\n\t\tpageHeight,\n\t\toptions.screenshotClipBuffer,\n\t);\n\n\tconst scale = adaptor.screenshotClipScale ?? 1;\n\n\t// Capture the focused state first, while focus is genuine: re-focusing the element afterwards\n\t// cannot always restore it (the host of a closed shadow root cannot push focus back inside), so\n\t// the focused frame must be taken before blurring.\n\tconst focusedScreenshot = await adaptor.screenshotClip(focusedClip, scale);\n\n\tawait adaptor.evaluate(blurScript, handle);\n\n\t// Measure the unfocused rect after blurring rather than reusing a stale baseline rect: a :focus\n\t// rule may have moved the element, and a fixed-position element's page-relative rect changes with\n\t// every scroll.\n\tconst unfocusedRect = await adaptor.evaluate(elementRectScript, handle);\n\tconst unfocusedClip = bufferedClip(\n\t\tunfocusedRect,\n\t\tpageWidth,\n\t\tpageHeight,\n\t\toptions.screenshotClipBuffer,\n\t);\n\n\tconst unfocusedScreenshot = await adaptor.screenshotClip(unfocusedClip, scale);\n\n\tconst unfocusedStyles = await adaptor.evaluate(elementStylesScript, handle, [\n\t\t...options.styleProps,\n\t]);\n\n\t// Restore focus so the next Tab advances from this element rather than restarting traversal. (A\n\t// closed-shadow host can't be re-focused into the root; the loop still progresses because Tab\n\t// re-enters from there.)\n\tawait adaptor.evaluate(focusScript, handle);\n\n\treturn {\n\t\tfocusedScreenshot,\n\t\tunfocusedScreenshot,\n\t\tunfocusedStyles,\n\t\tfocusedRect,\n\t\tunfocusedRect,\n\t\tfocusedClip,\n\t\tunfocusedClip,\n\t\tscale,\n\t};\n}\n"],"mappings":"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","names":["opacity","bufferedClip","bufferedClip","DEFAULT_SCREENSHOT_SETTLE_DELAY"]}
|
package/dist/types.d.ts
CHANGED
|
@@ -12,9 +12,25 @@ export type ActiveElementInfo = {
|
|
|
12
12
|
html: string;
|
|
13
13
|
styles: StyleSnapshot;
|
|
14
14
|
rect: Rect;
|
|
15
|
+
/**
|
|
16
|
+
* The element sits on a fixed or sticky ancestor (or is one), so its page position depends on
|
|
17
|
+
* scrolling rather than on where it is in the page.
|
|
18
|
+
*/
|
|
19
|
+
pinned?: boolean;
|
|
20
|
+
/** The element is inside an open dialog, where focus management may legitimately leave the page order. */
|
|
21
|
+
inDialog?: boolean;
|
|
15
22
|
};
|
|
16
23
|
export type Capability = "baselineStyles" | "contextSignals" | "obscuring" | "screenshot" | "unfocusedPair";
|
|
17
|
-
|
|
24
|
+
/** `stuck`: focus never came back out of an iframe. */
|
|
25
|
+
export type SessionEndReason = "completed" | "lostFocus" | "navigation" | "failed" | "stuck";
|
|
26
|
+
export type SessionEndDetail = {
|
|
27
|
+
/**
|
|
28
|
+
* Marker index of the element whose second visit ended the session, or `null` when the session
|
|
29
|
+
* ended any other way. A `"completed"` session with an index here ended on a cycle rather than by
|
|
30
|
+
* tabbing off the end of the page.
|
|
31
|
+
*/
|
|
32
|
+
revisitedIndex: number | null;
|
|
33
|
+
};
|
|
18
34
|
export type UnfocusedPair = {
|
|
19
35
|
focusedScreenshot: Uint8Array;
|
|
20
36
|
unfocusedScreenshot: Uint8Array;
|
|
@@ -67,5 +83,5 @@ export type TabConsumer = {
|
|
|
67
83
|
readonly capabilities: ReadonlySet<Capability>;
|
|
68
84
|
onSessionStart?(session: TabSessionHandle): void | Promise<void>;
|
|
69
85
|
onTabStop(snapshot: TabStopSnapshot, session: TabSessionHandle): void | Promise<void>;
|
|
70
|
-
onSessionEnd?(reason: SessionEndReason): void;
|
|
86
|
+
onSessionEnd?(reason: SessionEndReason, detail: SessionEndDetail): void;
|
|
71
87
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@a11y-pulse/tab-orchestrator",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Shared Tab-session runner for A11y Pulse keyboard audits.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -47,8 +47,8 @@
|
|
|
47
47
|
"@a11y-pulse/context-change-on-focus-audit": "*",
|
|
48
48
|
"@a11y-pulse/focus-appearance-audit": "*",
|
|
49
49
|
"@a11y-pulse/focus-not-obscured-audit": "*",
|
|
50
|
-
"happy-dom": "^
|
|
50
|
+
"happy-dom": "^21.0.1",
|
|
51
51
|
"playwright-core": "^1.63.0",
|
|
52
|
-
"puppeteer": "^25.
|
|
52
|
+
"puppeteer": "^25.12.0"
|
|
53
53
|
}
|
|
54
54
|
}
|