@a11y-pulse/tab-orchestrator 0.3.0 → 0.4.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/README.md +4 -4
- package/dist/adaptor.d.ts +6 -8
- package/dist/adaptors/puppeteer.js +13 -9
- package/dist/adaptors/puppeteer.js.map +1 -1
- package/dist/audit-scaffold.d.ts +6 -7
- package/dist/browser-scripts.d.ts +31 -37
- package/dist/capture-screenshot.d.ts +2 -2
- package/dist/clip.d.ts +3 -3
- package/dist/context-signals.d.ts +3 -4
- package/dist/get-selector.d.ts +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +39 -88
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +3 -3
- package/dist/unfocused-pair.d.ts +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
[](https://github.com/A11y-Pulse/audits/actions/workflows/ci.yml)
|
|
5
5
|
[](./LICENSE.md)
|
|
6
6
|
|
|
7
|
-
A shared Tab-session runner for A11y Pulse's keyboard-driven accessibility audits. It drives a page through its focusable elements once
|
|
7
|
+
A shared Tab-session runner for A11y Pulse's keyboard-driven accessibility audits. It drives a page through its focusable elements once (pressing Tab, tracking visited elements, capturing screenshots and styles on demand) while one or more **consumers** each score the tab stops for their own WCAG success criterion. This lets audits like [`@a11y-pulse/focus-appearance-audit`](../focus-appearance-audit) (WCAG 2.4.7) share a single tab loop with sibling audits instead of each re-tabbing the page from scratch.
|
|
8
8
|
|
|
9
9
|
It is not an audit itself: it has no opinion on what "passing" means. It only owns the mechanics of moving focus, tracking cycle completion, and capturing the primitives (computed styles, clipped screenshots, unfocused/focused pairs) that consumers need.
|
|
10
10
|
|
|
@@ -35,9 +35,9 @@ await orchestrator.run();
|
|
|
35
35
|
- `.attach(consumer)` registers a `TabConsumer`. Attaching after `run()` has started throws.
|
|
36
36
|
- `.run()` presses Tab repeatedly, dispatching each `TabStopSnapshot` to every still-attached consumer, until either a full tab cycle completes, focus is lost, navigation happens, or every consumer has disconnected. Calling it twice throws; calling it with zero attached consumers returns immediately without tabbing.
|
|
37
37
|
|
|
38
|
-
Each consumer declares the `capabilities` it needs (e.g. `baselineStyles`, `unfocusedPair`) and can disconnect independently once it has what it needs (say, after hitting its own element limit)
|
|
38
|
+
Each consumer declares the `capabilities` it needs (e.g. `baselineStyles`, `unfocusedPair`) and can disconnect independently once it has what it needs (say, after hitting its own element limit). The orchestrator keeps driving the remaining consumers. Per-stop work like unfocused-pair screenshots is only captured if a still-attached consumer actually asks for it that stop.
|
|
39
39
|
|
|
40
|
-
Most callers won't build a `TabConsumer` by hand
|
|
40
|
+
Most callers won't build a `TabConsumer` by hand. Audit packages export a `createXAudit(options)` helper that returns one, plus a `runXAudit(adaptor, options)` convenience wrapper that builds a private single-consumer orchestrator for you. See [`@a11y-pulse/focus-appearance-audit`'s "Shared tab session"](../focus-appearance-audit#shared-tab-session) section for a worked example.
|
|
41
41
|
|
|
42
42
|
## `BrowserAdaptor`
|
|
43
43
|
|
|
@@ -50,7 +50,7 @@ The orchestrator drives a page through an **adaptor**: a small interface of prim
|
|
|
50
50
|
| `disposeRef(ref)` | Releases a handle previously returned by `evaluateHandle`. |
|
|
51
51
|
| `pressTab()` | Presses the Tab key, advancing focus to the next focusable element. |
|
|
52
52
|
| `screenshotClip(clip)` | Screenshots a clipped region of the page (`{ x, y, width, height }`) and returns PNG bytes. |
|
|
53
|
-
| `ensureFocusReporting()` | Ensures the page reports focus for its lifetime
|
|
53
|
+
| `ensureFocusReporting()` | Ensures the page reports focus for its lifetime, in particular that `document.hasFocus()` works and `:focus` styles apply, even when the page is not the foreground tab/window. Must not throw. |
|
|
54
54
|
|
|
55
55
|
The orchestrator is the only thing that mutates the page through this adaptor during a session; consumers score snapshots, they don't call `pressTab`, take screenshots, or install observers themselves.
|
|
56
56
|
|
package/dist/adaptor.d.ts
CHANGED
|
@@ -9,8 +9,8 @@ export type Rect = {
|
|
|
9
9
|
*/
|
|
10
10
|
export type ElementRef = unknown;
|
|
11
11
|
/**
|
|
12
|
-
* A framework-specific adaptor that provides the primitives needed by the audit. An adaptor wraps
|
|
13
|
-
*
|
|
12
|
+
* A framework-specific adaptor that provides the primitives needed by the audit. An adaptor wraps a
|
|
13
|
+
* single page in a web browser.
|
|
14
14
|
*/
|
|
15
15
|
export interface BrowserAdaptor {
|
|
16
16
|
/**
|
|
@@ -30,10 +30,9 @@ export interface BrowserAdaptor {
|
|
|
30
30
|
*/
|
|
31
31
|
pressTab(): Promise<void>;
|
|
32
32
|
/**
|
|
33
|
-
* Screenshot a clipped region of the page. `scale` is the device scale
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
* replace it). The caller is responsible for the page being at a known
|
|
33
|
+
* Screenshot a clipped region of the page. `scale` is the device scale factor (bitmap pixels per
|
|
34
|
+
* CSS pixel), multiplied against whatever deviceScaleFactor the page itself currently has
|
|
35
|
+
* (Puppeteer does not replace it). The caller is responsible for the page being at a known
|
|
37
36
|
* deviceScaleFactor before invoking this.
|
|
38
37
|
*/
|
|
39
38
|
screenshotClip(clip: Rect, scale?: number): Promise<Uint8Array>;
|
|
@@ -41,8 +40,7 @@ export interface BrowserAdaptor {
|
|
|
41
40
|
readonly screenshotClipScale?: number;
|
|
42
41
|
/**
|
|
43
42
|
* Ensure the page reports focus for its lifetime. In particular, `document.hasFocus()` must
|
|
44
|
-
* function correctly and :focus styles must be applied, even when the page is not the
|
|
45
|
-
* foreground.
|
|
43
|
+
* function correctly and :focus styles must be applied, even when the page is not the foreground.
|
|
46
44
|
*
|
|
47
45
|
* This function must not throw.
|
|
48
46
|
*/
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/adaptors/puppeteer.ts
|
|
2
|
-
var
|
|
2
|
+
var focusEmulationSessions = /* @__PURE__ */ new WeakMap();
|
|
3
3
|
var PuppeteerAdaptor = class {
|
|
4
4
|
constructor(page) {
|
|
5
5
|
this.page = page;
|
|
@@ -29,20 +29,24 @@ var PuppeteerAdaptor = class {
|
|
|
29
29
|
});
|
|
30
30
|
}
|
|
31
31
|
async ensureFocusReporting() {
|
|
32
|
-
if (focusEmulationEnabled.has(this.page)) {
|
|
33
|
-
return;
|
|
34
|
-
}
|
|
35
32
|
try {
|
|
33
|
+
const existing = focusEmulationSessions.get(this.page);
|
|
34
|
+
if (existing) {
|
|
35
|
+
await existing.send("Emulation.setFocusEmulationEnabled", {
|
|
36
|
+
enabled: false
|
|
37
|
+
});
|
|
38
|
+
await existing.send("Emulation.setFocusEmulationEnabled", {
|
|
39
|
+
enabled: true
|
|
40
|
+
});
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
36
43
|
const client = await this.page.createCDPSession();
|
|
37
44
|
await client.send("Emulation.setFocusEmulationEnabled", {
|
|
38
45
|
enabled: true
|
|
39
46
|
});
|
|
40
|
-
|
|
47
|
+
focusEmulationSessions.set(this.page, client);
|
|
41
48
|
} catch (error) {
|
|
42
|
-
console.warn(
|
|
43
|
-
"Could not enable focus emulation for the tab orchestrator",
|
|
44
|
-
{ error }
|
|
45
|
-
);
|
|
49
|
+
console.warn("Could not enable focus emulation for the tab orchestrator", { error });
|
|
46
50
|
}
|
|
47
51
|
}
|
|
48
52
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/adaptors/puppeteer.ts"],"sourcesContent":["import type { JSHandle, Page } from \"puppeteer\";\nimport type { BrowserAdaptor, ElementRef, Rect } from \"../adaptor\";\n\n//
|
|
1
|
+
{"version":3,"sources":["../../src/adaptors/puppeteer.ts"],"sourcesContent":["import type { CDPSession, JSHandle, Page } from \"puppeteer\";\nimport type { BrowserAdaptor, ElementRef, Rect } from \"../adaptor\";\n\n// The CDP session holding focus emulation for each page. A fresh adaptor is created per audit run,\n// but a page often outlives one run, so without this each run would open another CDP session that\n// is never detached. The session is kept attached for the page's lifetime: Chrome clears the\n// emulation when the CDP client disconnects.\nconst focusEmulationSessions = new WeakMap<Page, CDPSession>();\n\n/** A BrowserAdaptor backed by a Puppeteer Page. */\nexport class PuppeteerAdaptor implements BrowserAdaptor {\n\treadonly screenshotClipScale = 2;\n\n\tconstructor(private readonly page: Page) {}\n\n\tevaluate<T>(\n\t\t// biome-ignore lint/suspicious/noExplicitAny: the page-evaluated fn accepts arbitrary serialised args and element handles.\n\t\tfn: (...args: any[]) => T | Promise<T>,\n\t\t...args: unknown[]\n\t): Promise<T> {\n\t\t// Puppeteer resolves any JSHandle passed as an arg to the live element.\n\t\treturn this.page.evaluate(fn as never, ...(args as never[])) as Promise<T>;\n\t}\n\n\tevaluateHandle(fn: () => Element | null): Promise<ElementRef> {\n\t\treturn this.page.evaluateHandle(fn);\n\t}\n\n\tasync disposeRef(ref: ElementRef): Promise<void> {\n\t\tawait (ref as JSHandle).dispose();\n\t}\n\n\tasync pressTab(): Promise<void> {\n\t\tawait this.page.keyboard.press(\"Tab\");\n\t}\n\n\tasync screenshotClip(clip: Rect, scale = 1): Promise<Uint8Array> {\n\t\treturn (await this.page.screenshot({\n\t\t\ttype: \"png\",\n\t\t\toptimizeForSpeed: true,\n\t\t\tclip: {\n\t\t\t\t...clip,\n\t\t\t\tscale,\n\t\t\t},\n\t\t})) as Uint8Array;\n\t}\n\n\tasync ensureFocusReporting(): Promise<void> {\n\t\t// Pages only report focus while they are the foreground tab. Since this browser instance is\n\t\t// likely running in the background, we need to enable focus emulation to get accurate results.\n\t\t//\n\t\t// Note this function does not throw if enabling emulation fails; it is a best-effort.\n\t\ttry {\n\t\t\tconst existing = focusEmulationSessions.get(this.page);\n\n\t\t\tif (existing) {\n\t\t\t\t// Chromium ignores a redundant enable, so re-asserting has to toggle.\n\t\t\t\tawait existing.send(\"Emulation.setFocusEmulationEnabled\", {\n\t\t\t\t\tenabled: false,\n\t\t\t\t});\n\t\t\t\tawait existing.send(\"Emulation.setFocusEmulationEnabled\", {\n\t\t\t\t\tenabled: true,\n\t\t\t\t});\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst client = await this.page.createCDPSession();\n\t\t\tawait client.send(\"Emulation.setFocusEmulationEnabled\", {\n\t\t\t\tenabled: true,\n\t\t\t});\n\t\t\tfocusEmulationSessions.set(this.page, client);\n\t\t} catch (error) {\n\t\t\tconsole.warn(\"Could not enable focus emulation for the tab orchestrator\", { error });\n\t\t}\n\t}\n}\n"],"mappings":";AAOA,IAAM,yBAAyB,oBAAI,QAA0B;AAGtD,IAAM,mBAAN,MAAiD;AAAA,EAGvD,YAA6B,MAAY;AAAZ;AAAA,EAAa;AAAA,EAAb;AAAA,EAFpB,sBAAsB;AAAA,EAI/B,SAEC,OACG,MACU;AAEb,WAAO,KAAK,KAAK,SAAS,IAAa,GAAI,IAAgB;AAAA,EAC5D;AAAA,EAEA,eAAe,IAA+C;AAC7D,WAAO,KAAK,KAAK,eAAe,EAAE;AAAA,EACnC;AAAA,EAEA,MAAM,WAAW,KAAgC;AAChD,UAAO,IAAiB,QAAQ;AAAA,EACjC;AAAA,EAEA,MAAM,WAA0B;AAC/B,UAAM,KAAK,KAAK,SAAS,MAAM,KAAK;AAAA,EACrC;AAAA,EAEA,MAAM,eAAe,MAAY,QAAQ,GAAwB;AAChE,WAAQ,MAAM,KAAK,KAAK,WAAW;AAAA,MAClC,MAAM;AAAA,MACN,kBAAkB;AAAA,MAClB,MAAM;AAAA,QACL,GAAG;AAAA,QACH;AAAA,MACD;AAAA,IACD,CAAC;AAAA,EACF;AAAA,EAEA,MAAM,uBAAsC;AAK3C,QAAI;AACH,YAAM,WAAW,uBAAuB,IAAI,KAAK,IAAI;AAErD,UAAI,UAAU;AAEb,cAAM,SAAS,KAAK,sCAAsC;AAAA,UACzD,SAAS;AAAA,QACV,CAAC;AACD,cAAM,SAAS,KAAK,sCAAsC;AAAA,UACzD,SAAS;AAAA,QACV,CAAC;AAED;AAAA,MACD;AAEA,YAAM,SAAS,MAAM,KAAK,KAAK,iBAAiB;AAChD,YAAM,OAAO,KAAK,sCAAsC;AAAA,QACvD,SAAS;AAAA,MACV,CAAC;AACD,6BAAuB,IAAI,KAAK,MAAM,MAAM;AAAA,IAC7C,SAAS,OAAO;AACf,cAAQ,KAAK,6DAA6D,EAAE,MAAM,CAAC;AAAA,IACpF;AAAA,EACD;AACD;","names":[]}
|
package/dist/audit-scaffold.d.ts
CHANGED
|
@@ -22,13 +22,13 @@ export type BaseAuditSummary = {
|
|
|
22
22
|
/** True if the audit stopped early after hitting `failedElementLimit`. */
|
|
23
23
|
reachedFailedElementLimit: boolean;
|
|
24
24
|
/**
|
|
25
|
-
* True if the audit returned early because `timeout` elapsed. The results
|
|
26
|
-
*
|
|
25
|
+
* True if the audit returned early because `timeout` elapsed. The results are whatever had been
|
|
26
|
+
* gathered when the deadline hit.
|
|
27
27
|
*/
|
|
28
28
|
timedOut: boolean;
|
|
29
29
|
/**
|
|
30
|
-
* Why the tab session ended, or `null` when this consumer disconnected
|
|
31
|
-
*
|
|
30
|
+
* Why the tab session ended, or `null` when this consumer disconnected itself (element limit,
|
|
31
|
+
* failed-element limit, or timeout).
|
|
32
32
|
*/
|
|
33
33
|
sessionEnd: SessionEndReason | null;
|
|
34
34
|
};
|
|
@@ -43,8 +43,7 @@ export type AuditSelfDisconnect = {
|
|
|
43
43
|
clear(): void;
|
|
44
44
|
};
|
|
45
45
|
/**
|
|
46
|
-
* Arms an optional self-disconnecting timeout and tracks whether this
|
|
47
|
-
*
|
|
48
|
-
* apart from an external end.
|
|
46
|
+
* Arms an optional self-disconnecting timeout and tracks whether this consumer initiated its own
|
|
47
|
+
* disconnect, so `onSessionEnd` can tell that apart from an external end.
|
|
49
48
|
*/
|
|
50
49
|
export declare function createAuditSelfDisconnect(): AuditSelfDisconnect;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* IMPORTANT: Functions in this file are serialized and injected into the audited page, so they
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* IMPORTANT: Functions in this file are serialized and injected into the audited page, so they must
|
|
3
|
+
* not reference any symbols outside their own scope. This also means they cannot share code, so any
|
|
4
|
+
* common logic (like descending into shadow roots) must be duplicated.
|
|
5
5
|
*/
|
|
6
6
|
import type { Rect } from "./adaptor";
|
|
7
7
|
import type { StyleSnapshot } from "./types";
|
|
@@ -30,9 +30,9 @@ export type ActiveElementBase = {
|
|
|
30
30
|
index: number | null;
|
|
31
31
|
isBody: boolean;
|
|
32
32
|
/**
|
|
33
|
-
* The focused element is an <iframe>. Tabbing into a frame passes focus into
|
|
34
|
-
*
|
|
35
|
-
*
|
|
33
|
+
* The focused element is an <iframe>. Tabbing into a frame passes focus into its embedded
|
|
34
|
+
* document, but the parent's document.activeElement is the frame element itself, so the loop uses
|
|
35
|
+
* this to skip the frame as a tab stop.
|
|
36
36
|
*/
|
|
37
37
|
isIframe: boolean;
|
|
38
38
|
html: string;
|
|
@@ -40,9 +40,8 @@ export type ActiveElementBase = {
|
|
|
40
40
|
rect: Rect;
|
|
41
41
|
};
|
|
42
42
|
/**
|
|
43
|
-
* Read the currently focused element (descending open shadow roots), returning
|
|
44
|
-
*
|
|
45
|
-
* styles, and page-relative rect.
|
|
43
|
+
* Read the currently focused element (descending open shadow roots), returning its marker index,
|
|
44
|
+
* opening-tag HTML (marker attribute stripped), focus-relevant styles, and page-relative rect.
|
|
46
45
|
*/
|
|
47
46
|
export declare function probeActiveElementScript(props: string[], markerAttr: string): ActiveElementBase;
|
|
48
47
|
export declare function activeElementHandleScript(): Element | null;
|
|
@@ -52,8 +51,8 @@ export declare function pageDimensionsScript(): {
|
|
|
52
51
|
};
|
|
53
52
|
export declare function elementRectScript(el: Element | null): Rect;
|
|
54
53
|
/**
|
|
55
|
-
* Whether something unrelated (e.g. a fixed cookie banner) covers the element's centre point, so
|
|
56
|
-
*
|
|
54
|
+
* Whether something unrelated (e.g. a fixed cookie banner) covers the element's centre point, so a
|
|
55
|
+
* screenshot of the element's box would show the covering element instead.
|
|
57
56
|
*/
|
|
58
57
|
export declare function isCenterObscuredScript(el: Element | null): boolean;
|
|
59
58
|
/**
|
|
@@ -61,26 +60,18 @@ export declare function isCenterObscuredScript(el: Element | null): boolean;
|
|
|
61
60
|
* afterwards isn't captured mid-animation.
|
|
62
61
|
*/
|
|
63
62
|
export declare function scrollToCenterScript(el: Element | null): void;
|
|
64
|
-
/**
|
|
65
|
-
* Blur (unfocus) an element
|
|
66
|
-
*/
|
|
67
63
|
export declare function blurScript(el: Element | null): void;
|
|
68
|
-
/**
|
|
69
|
-
* Focus an element
|
|
70
|
-
*/
|
|
71
64
|
export declare function focusScript(el: Element | null): void;
|
|
72
65
|
/**
|
|
73
|
-
* Read focus-relevant computed styles from a given element. Unlike
|
|
74
|
-
*
|
|
75
|
-
* still works after the node has been blurred.
|
|
66
|
+
* Read focus-relevant computed styles from a given element. Unlike `probeActiveElementScript`, this
|
|
67
|
+
* does not use `document.activeElement`, so it still works after the node has been blurred.
|
|
76
68
|
*/
|
|
77
69
|
export declare function elementStylesScript(el: Element | null, props: string[]): StyleSnapshot;
|
|
78
70
|
/**
|
|
79
|
-
* Remove the marker attribute from all elements, blur the active element, and
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
* ends.
|
|
71
|
+
* Remove the marker attribute from all elements, blur the active element, and scroll back to the
|
|
72
|
+
* top. This is a best-effort attempt to reset state between audits: the tab loop scrolls later
|
|
73
|
+
* elements into view as it walks the page (see scrollToCenterScript), and nothing else undoes that
|
|
74
|
+
* once the session ends.
|
|
84
75
|
*/
|
|
85
76
|
export declare function clearMarkersScript(markerAttr: string): void;
|
|
86
77
|
export type MeasureObscuringResult = {
|
|
@@ -93,9 +84,9 @@ export type MeasureObscuringResult = {
|
|
|
93
84
|
hasObscurer: boolean;
|
|
94
85
|
};
|
|
95
86
|
/**
|
|
96
|
-
* Hit-test whether author-created content entirely hides the focused element.
|
|
97
|
-
*
|
|
98
|
-
*
|
|
87
|
+
* Hit-test whether author-created content entirely hides the focused element. Marks a single
|
|
88
|
+
* covering element with `obscurerAttr` when containment confirms a full cover, so the host can
|
|
89
|
+
* resolve its selector via getSelector.
|
|
99
90
|
*/
|
|
100
91
|
export declare function measureObscuringScript(el: Element | null, obscurerAttr: string): MeasureObscuringResult;
|
|
101
92
|
/** Return the element marked as the obscurer, if any. */
|
|
@@ -116,8 +107,8 @@ declare global {
|
|
|
116
107
|
}
|
|
117
108
|
}
|
|
118
109
|
/**
|
|
119
|
-
* Install once: wrap window.open (record + inert stub), capture-phase submit
|
|
120
|
-
*
|
|
110
|
+
* Install once: wrap window.open (record + inert stub), capture-phase submit (record +
|
|
111
|
+
* preventDefault), track focusin and soft URL changes.
|
|
121
112
|
*/
|
|
122
113
|
export declare function installContextObserverScript(): void;
|
|
123
114
|
export type DrainContextObserverResult = {
|
|
@@ -132,20 +123,23 @@ export type DrainContextObserverResult = {
|
|
|
132
123
|
hasAttributed: boolean;
|
|
133
124
|
};
|
|
134
125
|
/**
|
|
135
|
-
* Drain observer signals for the current tab stop and reset per-stop flags.
|
|
136
|
-
*
|
|
137
|
-
*
|
|
126
|
+
* Drain observer signals for the current tab stop and reset per-stop flags. Classification of focus
|
|
127
|
+
* removal / redirection uses the post-Tab settle activeElement and the focusin history from this
|
|
128
|
+
* stop only.
|
|
138
129
|
*/
|
|
139
130
|
export declare function drainContextObserverScript(markerAttr: string): DrainContextObserverResult;
|
|
140
|
-
/**
|
|
141
|
-
*
|
|
131
|
+
/**
|
|
132
|
+
* Return the element marked as attributed by drainContextObserverScript (the element that received
|
|
133
|
+
* a focus event this stop and then lost it), if any.
|
|
134
|
+
*/
|
|
142
135
|
export declare function attributedHandleScript(): Element | null;
|
|
143
136
|
export declare function clearAttributedScript(): void;
|
|
144
137
|
/**
|
|
145
|
-
* Drop focusin history accumulated after drain (e.g. pixel-diff blur/refocus)
|
|
146
|
-
*
|
|
138
|
+
* Drop focusin history accumulated after drain (e.g. pixel-diff blur/refocus) so the next tab stop
|
|
139
|
+
* does not mis-attribute F55 or redirects.
|
|
147
140
|
*/
|
|
148
141
|
export declare function clearContextFocusInsScript(): void;
|
|
149
142
|
/** Current location.href for navigation comparison in the host. */
|
|
150
143
|
export declare function locationHrefScript(): string;
|
|
144
|
+
export declare function hasFocusScript(): boolean;
|
|
151
145
|
export {};
|
|
@@ -4,7 +4,7 @@ export type CaptureScreenshotOptions = {
|
|
|
4
4
|
};
|
|
5
5
|
/**
|
|
6
6
|
* Screenshot the element `handle` currently refers to, clipped to its box plus
|
|
7
|
-
* `screenshotClipBuffer` padding. The caller owns `handle` and must dispose it
|
|
8
|
-
*
|
|
7
|
+
* `screenshotClipBuffer` padding. The caller owns `handle` and must dispose it after this promise
|
|
8
|
+
* settles.
|
|
9
9
|
*/
|
|
10
10
|
export declare function captureScreenshot(adaptor: BrowserAdaptor, options: CaptureScreenshotOptions, handle: ElementRef): Promise<Uint8Array>;
|
package/dist/clip.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Rect } from "./adaptor";
|
|
2
2
|
/**
|
|
3
|
-
* A screenshot clip framing `rect` with up to `buffer` pixels of padding on each side, shrunk
|
|
4
|
-
*
|
|
5
|
-
*
|
|
3
|
+
* A screenshot clip framing `rect` with up to `buffer` pixels of padding on each side, shrunk where
|
|
4
|
+
* the page edges leave less room. The padding allows for indicators drawn just outside the element
|
|
5
|
+
* box (outlines, shadows) and for anti-aliasing.
|
|
6
6
|
*/
|
|
7
7
|
export declare function bufferedClip(rect: Rect, pageWidth: number, pageHeight: number, buffer: number): Rect;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Whether focus destination Y is inside the subtree of intended focus X
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* same-subtree, not theft.
|
|
2
|
+
* Whether focus destination Y is inside the subtree of intended focus X (descendant or ancestor),
|
|
3
|
+
* which is legitimate delegation rather than theft. Walks across open shadow roots so focusing into
|
|
4
|
+
* a host's shadow content is same-subtree, not theft.
|
|
6
5
|
*/
|
|
7
6
|
export declare function isSameFocusSubtree(intended: Element, settled: Element): boolean;
|
package/dist/get-selector.d.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*
|
|
6
6
|
* Mirrors axe-core's approach: each level contributes the fewest features that discriminate the
|
|
7
7
|
* element (a unique id, the rarest single class, or an :nth-of-type position), and the walk stops
|
|
8
|
-
* as soon as the joined selector matches exactly one element. Selectors stay short because of
|
|
9
|
-
*
|
|
8
|
+
* as soon as the joined selector matches exactly one element. Selectors stay short because of that
|
|
9
|
+
* early stop, not because of truncation; the length budget is only a backstop.
|
|
10
10
|
*/
|
|
11
11
|
export declare function getSelector(root: Node | null): string;
|
package/dist/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ export type { BrowserAdaptor, ElementRef, Rect } from "./adaptor";
|
|
|
2
2
|
export type { AuditSelfDisconnect, BaseAuditOptions, BaseAuditSummary, } from "./audit-scaffold";
|
|
3
3
|
export { createAuditSelfDisconnect, DEFAULT_ELEMENT_LIMIT, DEFAULT_FAILED_ELEMENT_LIMIT, DEFAULT_SCREENSHOT_SETTLE_DELAY, DEFAULT_TIMEOUT, } from "./audit-scaffold";
|
|
4
4
|
export type { ActiveElementBase, BaselinePayload } from "./browser-scripts";
|
|
5
|
-
export { activeElementHandleScript, baselineScript, blurScript, clearMarkersScript, elementRectScript, elementStylesScript, focusScript, isCenterObscuredScript, pageDimensionsScript, probeActiveElementScript, scrollToCenterScript, } from "./browser-scripts";
|
|
5
|
+
export { activeElementHandleScript, baselineScript, blurScript, clearMarkersScript, elementRectScript, elementStylesScript, focusScript, hasFocusScript, isCenterObscuredScript, pageDimensionsScript, probeActiveElementScript, scrollToCenterScript, } from "./browser-scripts";
|
|
6
6
|
export { bufferedClip } from "./clip";
|
|
7
7
|
export { FOCUS_STYLE_PROPERTIES } from "./focus-style";
|
|
8
8
|
export { getSelector } from "./get-selector";
|
package/dist/index.js
CHANGED
|
@@ -226,14 +226,8 @@ function isCenterObscuredScript(el) {
|
|
|
226
226
|
return false;
|
|
227
227
|
}
|
|
228
228
|
const rect = el.getBoundingClientRect();
|
|
229
|
-
const x = Math.min(
|
|
230
|
-
|
|
231
|
-
window.innerWidth - 1
|
|
232
|
-
);
|
|
233
|
-
const y = Math.min(
|
|
234
|
-
Math.max(rect.top + rect.height / 2, 0),
|
|
235
|
-
window.innerHeight - 1
|
|
236
|
-
);
|
|
229
|
+
const x = Math.min(Math.max(rect.left + rect.width / 2, 0), window.innerWidth - 1);
|
|
230
|
+
const y = Math.min(Math.max(rect.top + rect.height / 2, 0), window.innerHeight - 1);
|
|
237
231
|
let hit = document.elementFromPoint(x, y);
|
|
238
232
|
while (hit?.shadowRoot) {
|
|
239
233
|
const inner = hit.shadowRoot.elementFromPoint(x, y);
|
|
@@ -319,9 +313,7 @@ function measureObscuringScript(el, obscurerAttr) {
|
|
|
319
313
|
if (!el) {
|
|
320
314
|
return empty;
|
|
321
315
|
}
|
|
322
|
-
for (const marked of Array.from(
|
|
323
|
-
document.querySelectorAll(`[${obscurerAttr}]`)
|
|
324
|
-
)) {
|
|
316
|
+
for (const marked of Array.from(document.querySelectorAll(`[${obscurerAttr}]`))) {
|
|
325
317
|
marked.removeAttribute(obscurerAttr);
|
|
326
318
|
}
|
|
327
319
|
const rect = el.getBoundingClientRect();
|
|
@@ -463,9 +455,7 @@ function obscurerHandleScript() {
|
|
|
463
455
|
return document.querySelector("[data-a11y-obscurer]");
|
|
464
456
|
}
|
|
465
457
|
function clearObscurerScript(obscurerAttr) {
|
|
466
|
-
for (const marked of Array.from(
|
|
467
|
-
document.querySelectorAll(`[${obscurerAttr}]`)
|
|
468
|
-
)) {
|
|
458
|
+
for (const marked of Array.from(document.querySelectorAll(`[${obscurerAttr}]`))) {
|
|
469
459
|
marked.removeAttribute(obscurerAttr);
|
|
470
460
|
}
|
|
471
461
|
}
|
|
@@ -551,9 +541,7 @@ function drainContextObserverScript(markerAttr) {
|
|
|
551
541
|
if (!state) {
|
|
552
542
|
return empty;
|
|
553
543
|
}
|
|
554
|
-
for (const marked of Array.from(
|
|
555
|
-
document.querySelectorAll(`[${attributedAttr}]`)
|
|
556
|
-
)) {
|
|
544
|
+
for (const marked of Array.from(document.querySelectorAll(`[${attributedAttr}]`))) {
|
|
557
545
|
marked.removeAttribute(attributedAttr);
|
|
558
546
|
}
|
|
559
547
|
const openedWindow = state.openedWindow;
|
|
@@ -568,9 +556,7 @@ function drainContextObserverScript(markerAttr) {
|
|
|
568
556
|
while (active?.shadowRoot?.activeElement) {
|
|
569
557
|
active = active.shadowRoot.activeElement;
|
|
570
558
|
}
|
|
571
|
-
const intended = focusIns.find(
|
|
572
|
-
(el) => el !== document.body && el !== document.documentElement
|
|
573
|
-
) ?? null;
|
|
559
|
+
const intended = focusIns.find((el) => el !== document.body && el !== document.documentElement) ?? null;
|
|
574
560
|
const openingHtml = (node) => {
|
|
575
561
|
const clone = node.cloneNode(false);
|
|
576
562
|
clone.removeAttribute(markerAttr);
|
|
@@ -628,9 +614,7 @@ function attributedHandleScript() {
|
|
|
628
614
|
return document.querySelector("[data-a11y-ctx-attr]");
|
|
629
615
|
}
|
|
630
616
|
function clearAttributedScript() {
|
|
631
|
-
for (const marked of Array.from(
|
|
632
|
-
document.querySelectorAll("[data-a11y-ctx-attr]")
|
|
633
|
-
)) {
|
|
617
|
+
for (const marked of Array.from(document.querySelectorAll("[data-a11y-ctx-attr]"))) {
|
|
634
618
|
marked.removeAttribute("data-a11y-ctx-attr");
|
|
635
619
|
}
|
|
636
620
|
}
|
|
@@ -646,16 +630,16 @@ function clearContextFocusInsScript() {
|
|
|
646
630
|
function locationHrefScript() {
|
|
647
631
|
return location.href;
|
|
648
632
|
}
|
|
633
|
+
function hasFocusScript() {
|
|
634
|
+
return document.hasFocus();
|
|
635
|
+
}
|
|
649
636
|
|
|
650
637
|
// src/clip.ts
|
|
651
638
|
function bufferedClip(rect, pageWidth, pageHeight, buffer) {
|
|
652
639
|
const left = Math.max(0, Math.min(buffer, rect.x));
|
|
653
640
|
const top = Math.max(0, Math.min(buffer, rect.y));
|
|
654
641
|
const right = Math.max(0, Math.min(buffer, pageWidth - rect.x - rect.width));
|
|
655
|
-
const bottom = Math.max(
|
|
656
|
-
0,
|
|
657
|
-
Math.min(buffer, pageHeight - rect.y - rect.height)
|
|
658
|
-
);
|
|
642
|
+
const bottom = Math.max(0, Math.min(buffer, pageHeight - rect.y - rect.height));
|
|
659
643
|
return {
|
|
660
644
|
x: Math.max(0, rect.x - left),
|
|
661
645
|
y: Math.max(0, rect.y - top),
|
|
@@ -832,12 +816,7 @@ function getSelector(root) {
|
|
|
832
816
|
async function captureScreenshot(adaptor, options, handle) {
|
|
833
817
|
const { width: pageWidth, height: pageHeight } = await adaptor.evaluate(pageDimensionsScript);
|
|
834
818
|
const rect = await adaptor.evaluate(elementRectScript, handle);
|
|
835
|
-
const clip = bufferedClip(
|
|
836
|
-
rect,
|
|
837
|
-
pageWidth,
|
|
838
|
-
pageHeight,
|
|
839
|
-
options.screenshotClipBuffer
|
|
840
|
-
);
|
|
819
|
+
const clip = bufferedClip(rect, pageWidth, pageHeight, options.screenshotClipBuffer);
|
|
841
820
|
const scale = adaptor.screenshotClipScale ?? 1;
|
|
842
821
|
return adaptor.screenshotClip(clip, scale);
|
|
843
822
|
}
|
|
@@ -873,9 +852,7 @@ function truncateHtml(html, options = {}) {
|
|
|
873
852
|
async function captureUnfocusedPair(adaptor, options, handle) {
|
|
874
853
|
if (await adaptor.evaluate(isCenterObscuredScript, handle)) {
|
|
875
854
|
await adaptor.evaluate(scrollToCenterScript, handle);
|
|
876
|
-
await new Promise(
|
|
877
|
-
(resolve) => setTimeout(resolve, options.screenshotSettleDelay)
|
|
878
|
-
);
|
|
855
|
+
await new Promise((resolve) => setTimeout(resolve, options.screenshotSettleDelay));
|
|
879
856
|
}
|
|
880
857
|
const { width: pageWidth, height: pageHeight } = await adaptor.evaluate(pageDimensionsScript);
|
|
881
858
|
const focusedRect = await adaptor.evaluate(elementRectScript, handle);
|
|
@@ -895,10 +872,7 @@ async function captureUnfocusedPair(adaptor, options, handle) {
|
|
|
895
872
|
pageHeight,
|
|
896
873
|
options.screenshotClipBuffer
|
|
897
874
|
);
|
|
898
|
-
const unfocusedScreenshot = await adaptor.screenshotClip(
|
|
899
|
-
unfocusedClip,
|
|
900
|
-
scale
|
|
901
|
-
);
|
|
875
|
+
const unfocusedScreenshot = await adaptor.screenshotClip(unfocusedClip, scale);
|
|
902
876
|
const unfocusedStyles = await adaptor.evaluate(elementStylesScript, handle, [
|
|
903
877
|
...options.styleProps
|
|
904
878
|
]);
|
|
@@ -918,9 +892,7 @@ async function captureUnfocusedPair(adaptor, options, handle) {
|
|
|
918
892
|
// src/orchestrator.ts
|
|
919
893
|
function isContextDestroyedError(error) {
|
|
920
894
|
const message = error instanceof Error ? error.message : String(error);
|
|
921
|
-
return /Execution context was destroyed|Target closed|frame was detached|navigat/i.test(
|
|
922
|
-
message
|
|
923
|
-
);
|
|
895
|
+
return /Execution context was destroyed|Target closed|frame was detached|navigat/i.test(message);
|
|
924
896
|
}
|
|
925
897
|
var DEFAULT_SCREENSHOT_SETTLE_DELAY2 = 33;
|
|
926
898
|
var DEFAULT_SCREENSHOT_CLIP_BUFFER = 10;
|
|
@@ -990,11 +962,7 @@ function createTabOrchestrator(adaptor, options = {}) {
|
|
|
990
962
|
if (activeHandle === void 0) {
|
|
991
963
|
throw new Error("unfocusedPair capture not implemented");
|
|
992
964
|
}
|
|
993
|
-
pairByStop.current ??= captureUnfocusedPair(
|
|
994
|
-
adaptor,
|
|
995
|
-
resolved,
|
|
996
|
-
activeHandle
|
|
997
|
-
);
|
|
965
|
+
pairByStop.current ??= captureUnfocusedPair(adaptor, resolved, activeHandle);
|
|
998
966
|
return pairByStop.current;
|
|
999
967
|
},
|
|
1000
968
|
screenshotClip: () => {
|
|
@@ -1007,11 +975,7 @@ function createTabOrchestrator(adaptor, options = {}) {
|
|
|
1007
975
|
if (activeHandle === void 0) {
|
|
1008
976
|
throw new Error("screenshot capture not implemented");
|
|
1009
977
|
}
|
|
1010
|
-
screenshotByStop.current ??= captureScreenshot(
|
|
1011
|
-
adaptor,
|
|
1012
|
-
resolved,
|
|
1013
|
-
activeHandle
|
|
1014
|
-
);
|
|
978
|
+
screenshotByStop.current ??= captureScreenshot(adaptor, resolved, activeHandle);
|
|
1015
979
|
return screenshotByStop.current;
|
|
1016
980
|
}
|
|
1017
981
|
});
|
|
@@ -1046,6 +1010,13 @@ function createTabOrchestrator(adaptor, options = {}) {
|
|
|
1046
1010
|
baselineHref = href;
|
|
1047
1011
|
return { navigation: false, raw };
|
|
1048
1012
|
}
|
|
1013
|
+
async function documentHasFocus() {
|
|
1014
|
+
if (await adaptor.evaluate(hasFocusScript)) {
|
|
1015
|
+
return true;
|
|
1016
|
+
}
|
|
1017
|
+
await adaptor.ensureFocusReporting();
|
|
1018
|
+
return adaptor.evaluate(hasFocusScript);
|
|
1019
|
+
}
|
|
1049
1020
|
let failure;
|
|
1050
1021
|
try {
|
|
1051
1022
|
await adaptor.ensureFocusReporting();
|
|
@@ -1075,8 +1046,7 @@ function createTabOrchestrator(adaptor, options = {}) {
|
|
|
1075
1046
|
pairByStop.current = null;
|
|
1076
1047
|
screenshotByStop.current = null;
|
|
1077
1048
|
lastHasAttributed = false;
|
|
1078
|
-
|
|
1079
|
-
if (!hasFocus) {
|
|
1049
|
+
if (!await documentHasFocus()) {
|
|
1080
1050
|
end("lostFocus");
|
|
1081
1051
|
break;
|
|
1082
1052
|
}
|
|
@@ -1092,13 +1062,10 @@ function createTabOrchestrator(adaptor, options = {}) {
|
|
|
1092
1062
|
if (attached.size === 0) {
|
|
1093
1063
|
break;
|
|
1094
1064
|
}
|
|
1065
|
+
const focusedAtMeasurement = await documentHasFocus();
|
|
1095
1066
|
let base;
|
|
1096
1067
|
try {
|
|
1097
|
-
base = await adaptor.evaluate(
|
|
1098
|
-
probeActiveElementScript,
|
|
1099
|
-
styleProps,
|
|
1100
|
-
MARKER_ATTR
|
|
1101
|
-
);
|
|
1068
|
+
base = await adaptor.evaluate(probeActiveElementScript, styleProps, MARKER_ATTR);
|
|
1102
1069
|
} catch (error) {
|
|
1103
1070
|
if (remainingHas("contextSignals") && isContextDestroyedError(error)) {
|
|
1104
1071
|
end("navigation");
|
|
@@ -1106,6 +1073,10 @@ function createTabOrchestrator(adaptor, options = {}) {
|
|
|
1106
1073
|
}
|
|
1107
1074
|
throw error;
|
|
1108
1075
|
}
|
|
1076
|
+
if (!focusedAtMeasurement && base !== null && !base.isBody) {
|
|
1077
|
+
end("lostFocus");
|
|
1078
|
+
break;
|
|
1079
|
+
}
|
|
1109
1080
|
let contextNav = null;
|
|
1110
1081
|
if (remainingHas("contextSignals")) {
|
|
1111
1082
|
const outcome = await checkContextNavigation();
|
|
@@ -1119,10 +1090,7 @@ function createTabOrchestrator(adaptor, options = {}) {
|
|
|
1119
1090
|
if (remainingHas("contextSignals")) {
|
|
1120
1091
|
let raw;
|
|
1121
1092
|
try {
|
|
1122
|
-
raw = contextNav?.raw ?? await adaptor.evaluate(
|
|
1123
|
-
drainContextObserverScript,
|
|
1124
|
-
MARKER_ATTR
|
|
1125
|
-
);
|
|
1093
|
+
raw = contextNav?.raw ?? await adaptor.evaluate(drainContextObserverScript, MARKER_ATTR);
|
|
1126
1094
|
} catch (error) {
|
|
1127
1095
|
if (!isContextDestroyedError(error)) {
|
|
1128
1096
|
throw error;
|
|
@@ -1131,9 +1099,7 @@ function createTabOrchestrator(adaptor, options = {}) {
|
|
|
1131
1099
|
break;
|
|
1132
1100
|
}
|
|
1133
1101
|
if (raw.hasAttributed) {
|
|
1134
|
-
const attributedRef = await adaptor.evaluateHandle(
|
|
1135
|
-
attributedHandleScript
|
|
1136
|
-
);
|
|
1102
|
+
const attributedRef = await adaptor.evaluateHandle(attributedHandleScript);
|
|
1137
1103
|
let selector;
|
|
1138
1104
|
try {
|
|
1139
1105
|
selector = await adaptor.evaluate(getSelector, attributedRef);
|
|
@@ -1210,10 +1176,7 @@ function createTabOrchestrator(adaptor, options = {}) {
|
|
|
1210
1176
|
if (remainingHas("contextSignals")) {
|
|
1211
1177
|
let signals;
|
|
1212
1178
|
try {
|
|
1213
|
-
const raw = contextNav?.raw ?? await adaptor.evaluate(
|
|
1214
|
-
drainContextObserverScript,
|
|
1215
|
-
MARKER_ATTR
|
|
1216
|
-
);
|
|
1179
|
+
const raw = contextNav?.raw ?? await adaptor.evaluate(drainContextObserverScript, MARKER_ATTR);
|
|
1217
1180
|
signals = {
|
|
1218
1181
|
openedWindow: raw.openedWindow,
|
|
1219
1182
|
submittedForm: raw.submittedForm,
|
|
@@ -1240,29 +1203,16 @@ function createTabOrchestrator(adaptor, options = {}) {
|
|
|
1240
1203
|
snapshot.contextSignals = { signals };
|
|
1241
1204
|
}
|
|
1242
1205
|
if (remainingHas("obscuring")) {
|
|
1243
|
-
let raw = await adaptor.evaluate(
|
|
1244
|
-
measureObscuringScript,
|
|
1245
|
-
ref,
|
|
1246
|
-
OBSCURER_ATTR
|
|
1247
|
-
);
|
|
1206
|
+
let raw = await adaptor.evaluate(measureObscuringScript, ref, OBSCURER_ATTR);
|
|
1248
1207
|
if (raw.fullyObscured) {
|
|
1249
|
-
await new Promise(
|
|
1250
|
-
|
|
1251
|
-
);
|
|
1252
|
-
raw = await adaptor.evaluate(
|
|
1253
|
-
measureObscuringScript,
|
|
1254
|
-
ref,
|
|
1255
|
-
OBSCURER_ATTR
|
|
1256
|
-
);
|
|
1208
|
+
await new Promise((resolve) => setTimeout(resolve, OBSCURING_RECHECK_DELAY_MS));
|
|
1209
|
+
raw = await adaptor.evaluate(measureObscuringScript, ref, OBSCURER_ATTR);
|
|
1257
1210
|
}
|
|
1258
1211
|
let obscuredBy = null;
|
|
1259
1212
|
if (raw.hasObscurer) {
|
|
1260
1213
|
const obscurer = await adaptor.evaluateHandle(obscurerHandleScript);
|
|
1261
1214
|
try {
|
|
1262
|
-
const obscurerSelector = await adaptor.evaluate(
|
|
1263
|
-
getSelector,
|
|
1264
|
-
obscurer
|
|
1265
|
-
);
|
|
1215
|
+
const obscurerSelector = await adaptor.evaluate(getSelector, obscurer);
|
|
1266
1216
|
obscuredBy = {
|
|
1267
1217
|
selector: obscurerSelector,
|
|
1268
1218
|
html: truncateHtml(raw.obscuredByHtml ?? "")
|
|
@@ -1337,6 +1287,7 @@ export {
|
|
|
1337
1287
|
elementStylesScript,
|
|
1338
1288
|
focusScript,
|
|
1339
1289
|
getSelector,
|
|
1290
|
+
hasFocusScript,
|
|
1340
1291
|
isCenterObscuredScript,
|
|
1341
1292
|
pageDimensionsScript,
|
|
1342
1293
|
probeActiveElementScript,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/audit-scaffold.ts","../src/browser-scripts.ts","../src/clip.ts","../src/focus-style.ts","../src/get-selector.ts","../src/capture-screenshot.ts","../src/truncate-html.ts","../src/unfocused-pair.ts","../src/orchestrator.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\n\t * are whatever had been 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\n\t * itself (element limit, 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(\n\t\ttimeout: number,\n\t\tsession: TabSessionHandle,\n\t\tonTimeout: () => void,\n\t): 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\n * consumer initiated its own disconnect, so `onSessionEnd` can tell that\n * 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\n * must not reference any symbols outside their own scope. This also means they cannot share code,\n * so any common logic (like descending into shadow roots) must be duplicated.\n */\n\nimport type { Rect } from \"./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)\n\t// while 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\n\t// \"change\" 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\t// Skip disabled elements\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\n\t\t\t// tabindex). The audit tabs through the page, so it never lands on them,\n\t\t\t// and counting them would waste the 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.\n\t\t// links inside a closed mega-menu). checkVisibility sees hiding applied by\n\t\t// ancestors, which per-element computed styles don't (`display` doesn't\n\t\t// inherit); fall back to the element's own styles where it's 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\n\t\t// is present, so the 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\n\t\t// snapshot is built with the 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\n\t * its embedded document, but the parent's document.activeElement is the frame\n\t * element itself, so the loop uses 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\n * its marker index, opening-tag HTML (marker attribute stripped), focus-relevant\n * styles, and page-relative rect.\n */\nexport function probeActiveElementScript(\n\tprops: string[],\n\tmarkerAttr: string,\n): 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;\n\t// drop the internal 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\n * a 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(\n\t\tMath.max(rect.left + rect.width / 2, 0),\n\t\twindow.innerWidth - 1,\n\t);\n\tconst y = Math.min(\n\t\tMath.max(rect.top + rect.height / 2, 0),\n\t\twindow.innerHeight - 1,\n\t);\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 * Scroll the element to the centre of the viewport, instantly so a screenshot taken immediately\n * afterwards isn't captured mid-animation.\n */\nexport function scrollToCenterScript(el: Element | null): void {\n\tel?.scrollIntoView({\n\t\tblock: \"center\",\n\t\tinline: \"nearest\",\n\t\tbehavior: \"instant\",\n\t});\n}\n\n/**\n * Blur (unfocus) an element\n */\nexport function blurScript(el: Element | null): void {\n\t(el as HTMLElement | null)?.blur();\n}\n\n/**\n * Focus an element\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\n * `probeActiveElementScript`, this does not use `document.activeElement`, so it\n * still works after the node has been blurred.\n */\nexport function elementStylesScript(\n\tel: Element | null,\n\tprops: string[],\n): 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\n\t// probeActiveElementScript: snapshots from the scripts are compared for\n\t// 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, 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\n * scroll back to the top. This is a best-effort attempt to reset state between\n * audits: the tab loop scrolls later elements into view as it walks the page\n * (see scrollToCenterScript), and nothing else undoes that once the session\n * 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.\n * Marks a single covering element with `obscurerAttr` when containment confirms\n * a full cover, so the host can 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(\n\t\tdocument.querySelectorAll(`[${obscurerAttr}]`),\n\t)) {\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 (\n\t\twidth > LARGE_ELEMENT_THRESHOLD_PX ||\n\t\theight > LARGE_ELEMENT_THRESHOLD_PX\n\t) {\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 (\n\t\t\t\tcandidate === el ||\n\t\t\t\tel.contains(candidate) ||\n\t\t\t\tcandidate.contains(el)\n\t\t\t) {\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 = (\n\t\tnode: Element,\n\t): \"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(\n\t\tdocument.querySelectorAll(`[${obscurerAttr}]`),\n\t)) {\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\n * (record + 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.\n * Classification of focus removal / redirection uses the post-Tab settle\n * activeElement and the focusin history from this stop only.\n */\nexport function drainContextObserverScript(\n\tmarkerAttr: string,\n): 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(\n\t\tdocument.querySelectorAll(`[${attributedAttr}]`),\n\t)) {\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(\n\t\t\t(el) => el !== document.body && el !== document.documentElement,\n\t\t) ?? 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 =\n\t\t!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 =\n\t\t\tcomposedContains(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/** Return the element marked as attributed by drainContextObserverScript (the\n * element that received a focus event this stop and then lost it), if any. */\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(\n\t\tdocument.querySelectorAll(\"[data-a11y-ctx-attr]\"),\n\t)) {\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)\n * so the next tab stop 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","import type { Rect } from \"./adaptor\";\n\n/**\n * A screenshot clip framing `rect` with up to `buffer` pixels of padding on each side, shrunk\n * where the page edges leave less room. The padding allows for indicators drawn just outside\n * the element box (outlines, shadows) and for anti-aliasing.\n */\nexport function bufferedClip(\n\trect: Rect,\n\tpageWidth: number,\n\tpageHeight: number,\n\tbuffer: number,\n): Rect {\n\tconst left = Math.max(0, Math.min(buffer, rect.x));\n\tconst top = Math.max(0, Math.min(buffer, rect.y));\n\tconst right = Math.max(0, Math.min(buffer, pageWidth - rect.x - rect.width));\n\tconst bottom = Math.max(\n\t\t0,\n\t\tMath.min(buffer, pageHeight - rect.y - rect.height),\n\t);\n\n\t// Clamp to non-negative origins: an element positioned partially off the top\n\t// or left of the document has a negative page-relative rect, and Puppeteer\n\t// rejects a screenshot clip with negative x/y.\n\treturn {\n\t\tx: Math.max(0, rect.x - left),\n\t\ty: Math.max(0, rect.y - top),\n\t\twidth: rect.width + left + right,\n\t\theight: rect.height + top + bottom,\n\t};\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","/**\n * Build a compact, hopefully-unique selector for any given element on the page. This function is\n * serialized and run in the page context, so it must not reference any symbols outside its own\n * scope.\n *\n * Mirrors axe-core's approach: each level contributes the fewest features that discriminate the\n * element (a unique id, the rarest single class, or an :nth-of-type position), and the walk stops\n * as soon as the joined selector matches exactly one element. Selectors stay short because of\n * that early stop, not because of truncation; the length budget is only a backstop.\n */\nexport function getSelector(root: Node | null): string {\n\tconst MAX_LENGTH = 128;\n\n\tconst esc = (value: string): string =>\n\t\ttypeof CSS !== \"undefined\" && typeof CSS.escape === \"function\"\n\t\t\t? CSS.escape(value)\n\t\t\t: value;\n\n\tconst tagName = (node: Node): string =>\n\t\tnode.nodeType === 1\n\t\t\t? node.nodeName.toLowerCase()\n\t\t\t: node.nodeName.toUpperCase().replace(/^#/, \"\");\n\n\t// A querySelectorAll-capable root (Document or ShadowRoot) for the element,\n\t// used to confirm uniqueness and class rarity. Absent on the plain objects\n\t// used in the serialisation unit tests, where we trust ids and walk to the\n\t// top instead of stopping early.\n\tconst rootOf = (element: Element): ParentNode | null => {\n\t\tconst node = (\n\t\t\telement as Element & { getRootNode?: () => Node }\n\t\t).getRootNode?.();\n\n\t\treturn node && typeof (node as ParentNode).querySelectorAll === \"function\"\n\t\t\t? (node as ParentNode)\n\t\t\t: null;\n\t};\n\n\tconst matchCount = (queryRoot: ParentNode, selector: string): number => {\n\t\ttry {\n\t\t\treturn queryRoot.querySelectorAll(selector).length;\n\t\t} catch {\n\t\t\t// A selector that fails to parse can't be relied on.\n\t\t\treturn Number.POSITIVE_INFINITY;\n\t\t}\n\t};\n\n\t// A candidate feature is only usable if it actually selects this element;\n\t// an escaping or engine mismatch (e.g. CSS string escapes in an attribute\n\t// value) could otherwise produce a selector that matches nothing — or a\n\t// different element entirely.\n\tconst safeMatches = (element: Element, selector: string): boolean => {\n\t\ttry {\n\t\t\treturn typeof element.matches === \"function\" && element.matches(selector);\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t};\n\n\tconst idIsUnique = (element: Element, id: string): boolean => {\n\t\tconst queryRoot = rootOf(element);\n\n\t\treturn queryRoot === null || matchCount(queryRoot, `#${esc(id)}`) === 1;\n\t};\n\n\t// A discriminating feature for one level of the selector: its CSS text plus\n\t// a predicate telling whether a sibling would match the same feature (used\n\t// to decide if an :nth-of-type pin is still needed).\n\ttype Feature = {\n\t\tfeature: string;\n\t\tsharedBy: (sibling: Element) => boolean;\n\t};\n\n\tconst classFeature = (cls: string): Feature => ({\n\t\tfeature: `.${esc(cls)}`,\n\t\tsharedBy: (sibling) => Array.from(sibling.classList ?? []).includes(cls),\n\t});\n\n\t// The single feature that narrows the element down the most: the class or\n\t// href attribute with the fewest matches in the root. Hrefs break ties\n\t// between structurally-identical content blocks, where classes and\n\t// :nth-of-type (which only pins position within one parent) cannot. Without\n\t// a queryable root there is no rarity information, so the first class\n\t// stands in.\n\tconst rarestFeature = (\n\t\telement: Element,\n\t\tqueryRoot: ParentNode | null,\n\t): Feature | null => {\n\t\tconst classes = Array.from(element.classList).slice(0, 8);\n\n\t\tif (!queryRoot) {\n\t\t\tconst cls = classes[0];\n\n\t\t\treturn cls === undefined ? null : classFeature(cls);\n\t\t}\n\n\t\tlet best: Feature | null = null;\n\t\tlet bestCount = Number.POSITIVE_INFINITY;\n\n\t\tfor (const cls of classes) {\n\t\t\tconst candidate = classFeature(cls);\n\n\t\t\tif (!safeMatches(element, candidate.feature)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst count = matchCount(queryRoot, candidate.feature);\n\n\t\t\tif (count < bestCount) {\n\t\t\t\tbest = candidate;\n\t\t\t\tbestCount = count;\n\t\t\t}\n\n\t\t\tif (count === 1) {\n\t\t\t\treturn best;\n\t\t\t}\n\t\t}\n\n\t\tconst href =\n\t\t\ttypeof element.getAttribute === \"function\"\n\t\t\t\t? element.getAttribute(\"href\")\n\t\t\t\t: null;\n\n\t\t// Skip hrefs with characters that change meaning inside a CSS string\n\t\t// (quotes, backslashes), and query strings, which are too volatile\n\t\t// across scans to pin a selector to. Fragments (#main) stay eligible —\n\t\t// skip links make stable discriminators.\n\t\tif (href && href.length <= 100 && !/[\"\\\\?]/.test(href)) {\n\t\t\tconst feature = `[href=\"${href}\"]`;\n\n\t\t\tif (\n\t\t\t\tsafeMatches(element, feature) &&\n\t\t\t\tmatchCount(queryRoot, feature) < bestCount\n\t\t\t) {\n\t\t\t\treturn {\n\t\t\t\t\tfeature,\n\t\t\t\t\tsharedBy: (sibling) =>\n\t\t\t\t\t\ttypeof sibling.getAttribute === \"function\" &&\n\t\t\t\t\t\tsibling.getAttribute(\"href\") === href,\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\treturn best;\n\t};\n\n\t// The element's 1-based position among same-tag siblings, and whether the\n\t// chosen feature fails to discriminate it from them (so the segment needs\n\t// an :nth-of-type pin). Siblings are absent on the serialisation test\n\t// nodes — there we skip the pin.\n\tconst positionAmongSiblings = (\n\t\telement: Element,\n\t\tsharedBy: (sibling: Element) => boolean,\n\t): { nth: number; ambiguous: boolean } => {\n\t\tconst siblings = (element.parentNode as ParentNode | null)?.children;\n\n\t\tif (!siblings) {\n\t\t\treturn { nth: 0, ambiguous: false };\n\t\t}\n\n\t\tlet nth = 0;\n\t\tlet count = 0;\n\t\tlet shared = 0;\n\n\t\tfor (const sibling of Array.from(siblings)) {\n\t\t\tif (sibling.nodeName !== element.nodeName) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tcount++;\n\n\t\t\tif (sibling === element) {\n\t\t\t\tnth = count;\n\t\t\t}\n\n\t\t\tif (sharedBy(sibling)) {\n\t\t\t\tshared++;\n\t\t\t}\n\t\t}\n\n\t\treturn { nth, ambiguous: shared > 1 };\n\t};\n\n\tconst segmentFor = (\n\t\telement: Element,\n\t\tqueryRoot: ParentNode | null,\n\t): { segment: string; anchored: boolean } => {\n\t\t// Only treat an id as an absolute anchor when it actually resolves to a\n\t\t// single element: pages with duplicate ids would otherwise yield a\n\t\t// selector (e.g. \"#dup>a\") that matches several elements.\n\t\tif (element.id && idIsUnique(element, element.id)) {\n\t\t\treturn { segment: `#${esc(element.id)}`, anchored: true };\n\t\t}\n\n\t\tconst feature = rarestFeature(element, queryRoot);\n\t\tlet segment = tagName(element) + (feature === null ? \"\" : feature.feature);\n\n\t\t// When same-tag siblings share the chosen feature, pin the position so the\n\t\t// segment selects exactly this element rather than all of them.\n\t\tconst { nth, ambiguous } = positionAmongSiblings(\n\t\t\telement,\n\t\t\tfeature === null ? () => true : feature.sharedBy,\n\t\t);\n\n\t\tif (ambiguous && nth > 0) {\n\t\t\tsegment += `:nth-of-type(${nth})`;\n\t\t}\n\n\t\treturn { segment, anchored: false };\n\t};\n\n\t// A minimal segment for when a level's full segment would blow the budget\n\t// (e.g. an enormous class name or a long href): just the tag, pinned by\n\t// position when same-tag siblings would make the bare tag ambiguous.\n\tconst degradedSegmentFor = (element: Element): string => {\n\t\tlet segment = tagName(element);\n\n\t\tconst { nth, ambiguous } = positionAmongSiblings(element, () => true);\n\n\t\tif (ambiguous && nth > 0) {\n\t\t\tsegment += `:nth-of-type(${nth})`;\n\t\t}\n\n\t\treturn segment;\n\t};\n\n\tconst segments: string[] = [];\n\tlet node: Node | null = root;\n\tconst queryRoot =\n\t\troot && root.nodeType === 1 ? rootOf(root as Element) : null;\n\n\ttry {\n\t\t// Walk only element nodes. Stopping at the first non-element parent (the\n\t\t// document, or a ShadowRoot when the target is inside an open shadow root)\n\t\t// keeps the selector within its tree and avoids reading `classList` off a\n\t\t// node that has none.\n\t\twhile (node && node.nodeType === 1) {\n\t\t\tconst element = node as Element;\n\t\t\tlet { segment, anchored } = segmentFor(element, queryRoot);\n\n\t\t\t// Keep within the budget (the +1 accounts for the \">\" that would join\n\t\t\t// this segment to the rest). A level whose full segment doesn't fit is\n\t\t\t// degraded to tag + position rather than discarded, so ancestor context\n\t\t\t// is never lost to one long feature.\n\t\t\tconst joinedWith = (candidate: string): number =>\n\t\t\t\tsegments.length\n\t\t\t\t\t? candidate.length + 1 + segments.join(\">\").length\n\t\t\t\t\t: candidate.length;\n\n\t\t\tif (joinedWith(segment) > MAX_LENGTH - 1) {\n\t\t\t\tsegment = degradedSegmentFor(element);\n\t\t\t\tanchored = false;\n\n\t\t\t\tif (joinedWith(segment) > MAX_LENGTH - 1) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tsegments.unshift(segment);\n\n\t\t\tif (anchored) {\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t// Stop as soon as the selector already identifies a single element —\n\t\t\t// this, not truncation, is what keeps selectors short.\n\t\t\tif (queryRoot && matchCount(queryRoot, segments.join(\">\")) === 1) {\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tnode = element.parentNode;\n\t\t}\n\t} catch {\n\t\t// A malformed node tree must never crash the audit; return what we have.\n\t}\n\n\treturn segments.join(\">\");\n}\n","import type { BrowserAdaptor, ElementRef } from \"./adaptor\";\nimport { elementRectScript, pageDimensionsScript } from \"./browser-scripts\";\nimport { bufferedClip } from \"./clip\";\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\n * after this promise settles.\n */\nexport async function captureScreenshot(\n\tadaptor: BrowserAdaptor,\n\toptions: CaptureScreenshotOptions,\n\thandle: ElementRef,\n): Promise<Uint8Array> {\n\tconst { width: pageWidth, height: pageHeight } =\n\t\tawait adaptor.evaluate(pageDimensionsScript);\n\tconst rect = await adaptor.evaluate(elementRectScript, handle);\n\tconst clip = bufferedClip(\n\t\trect,\n\t\tpageWidth,\n\t\tpageHeight,\n\t\toptions.screenshotClipBuffer,\n\t);\n\tconst scale = adaptor.screenshotClipScale ?? 1;\n\n\treturn adaptor.screenshotClip(clip, scale);\n}\n","const DEFAULT_MAX_ATTRIBUTE_VALUE_LENGTH = 50;\nconst DEFAULT_MAX_LENGTH = 300;\nconst ELLIPSIS = \"...\";\n\nexport type TruncateHtmlOptions = {\n\tmaxAttributeValueLength?: number;\n\tmaxLength?: number;\n};\n\n/**\n * Given an element's outerHTML, produce a truncated version suitable for display in results. For\n * memory efficiency, pass a shallow outerHTML i.e. `Node.cloneNode(false).outerHTML`.\n */\nexport function truncateHtml(\n\thtml: string,\n\toptions: TruncateHtmlOptions = {},\n): string {\n\tif (!html) {\n\t\treturn \"\";\n\t}\n\n\tconst maxAttributeValueLength =\n\t\toptions.maxAttributeValueLength ?? DEFAULT_MAX_ATTRIBUTE_VALUE_LENGTH;\n\tconst maxLength = options.maxLength ?? DEFAULT_MAX_LENGTH;\n\n\t// Keep only the opening tag\n\tconst closingBracket = html.indexOf(\">\");\n\tconst openingTag =\n\t\tclosingBracket === -1 ? html : html.slice(0, closingBracket + 1);\n\n\t// Shorten long quoted attribute values, preserving the quote style.\n\tconst trimmed = openingTag.replace(\n\t\t/=([\"'])([\\s\\S]*?)\\1/g,\n\t\t(match, quote: string, value: string) => {\n\t\t\tif (value.length <= maxAttributeValueLength) {\n\t\t\t\treturn match;\n\t\t\t}\n\n\t\t\treturn `=${quote}${value.slice(0, maxAttributeValueLength)}${ELLIPSIS}${quote}`;\n\t\t},\n\t);\n\n\tif (trimmed.length > maxLength) {\n\t\treturn `${trimmed.slice(0, maxLength)}${ELLIPSIS}`;\n\t}\n\n\treturn trimmed;\n}\n","import type { BrowserAdaptor, ElementRef } from \"./adaptor\";\nimport {\n\tblurScript,\n\telementRectScript,\n\telementStylesScript,\n\tfocusScript,\n\tisCenterObscuredScript,\n\tpageDimensionsScript,\n\tscrollToCenterScript,\n} from \"./browser-scripts\";\nimport { bufferedClip } from \"./clip\";\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\n * unfocused style snapshot 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\n\t// is exactly where fixed overlays (cookie banners, sticky footers) sit. If\n\t// something covers the element there, both screenshots would show the\n\t// overlay and no indicator could ever be detected. Centre the element in\n\t// the viewport first, then give the scroll a moment to settle.\n\tif (await adaptor.evaluate(isCenterObscuredScript, handle)) {\n\t\tawait adaptor.evaluate(scrollToCenterScript, handle);\n\n\t\tawait new Promise((resolve) =>\n\t\t\tsetTimeout(resolve, options.screenshotSettleDelay),\n\t\t);\n\t}\n\n\tconst { width: pageWidth, height: pageHeight } =\n\t\tawait 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\n\t// element afterwards cannot always restore it (the host of a closed shadow\n\t// root cannot push focus back inside), so the focused frame must be taken\n\t// 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\n\t// baseline rect: a :focus rule may have moved the element, and a\n\t// fixed-position element's page-relative rect changes with 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(\n\t\tunfocusedClip,\n\t\tscale,\n\t);\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\n\t// restarting traversal. (A closed-shadow host can't be re-focused into the\n\t// root; the loop still progresses because Tab 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","import type { BrowserAdaptor, ElementRef } from \"./adaptor\";\nimport {\n\ttype ActiveElementBase,\n\tactiveElementHandleScript,\n\tattributedHandleScript,\n\tbaselineScript,\n\tclearAttributedScript,\n\tclearContextFocusInsScript,\n\tclearMarkersScript,\n\tclearObscurerScript,\n\ttype DrainContextObserverResult,\n\tdrainContextObserverScript,\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 { getSelector } from \"./get-selector\";\nimport { truncateHtml } from \"./truncate-html\";\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 */\nfunction isContextDestroyedError(error: unknown): boolean {\n\tconst message = error instanceof Error ? error.message : String(error);\n\n\treturn /Execution context was destroyed|Target 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 =\n\t\t\t\toptions.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(\n\t\t\t\t\t\tadaptor,\n\t\t\t\t\t\tresolved,\n\t\t\t\t\t\tactiveHandle,\n\t\t\t\t\t);\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(\n\t\t\t\t\t\tadaptor,\n\t\t\t\t\t\tresolved,\n\t\t\t\t\t\tactiveHandle,\n\t\t\t\t\t);\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\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\tconst hasFocus = await adaptor.evaluate(() => document.hasFocus());\n\n\t\t\t\t\tif (!hasFocus) {\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\tlet base: ActiveElementBase | null;\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tbase = await adaptor.evaluate(\n\t\t\t\t\t\t\tprobeActiveElementScript,\n\t\t\t\t\t\t\tstyleProps,\n\t\t\t\t\t\t\tMARKER_ATTR,\n\t\t\t\t\t\t);\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\tremainingHas(\"contextSignals\") &&\n\t\t\t\t\t\t\tisContextDestroyedError(error)\n\t\t\t\t\t\t) {\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// 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(\n\t\t\t\t\t\t\t\t\t\tdrainContextObserverScript,\n\t\t\t\t\t\t\t\t\t\tMARKER_ATTR,\n\t\t\t\t\t\t\t\t\t));\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(\n\t\t\t\t\t\t\t\t\tattributedHandleScript,\n\t\t\t\t\t\t\t\t);\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 (\n\t\t\t\t\t\t\tremainingHas(\"baselineStyles\") &&\n\t\t\t\t\t\t\tactiveElement.index !== null\n\t\t\t\t\t\t) {\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(\n\t\t\t\t\t\t\t\t\t\tdrainContextObserverScript,\n\t\t\t\t\t\t\t\t\t\tMARKER_ATTR,\n\t\t\t\t\t\t\t\t\t));\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(\n\t\t\t\t\t\t\t\tmeasureObscuringScript,\n\t\t\t\t\t\t\t\tref,\n\t\t\t\t\t\t\t\tOBSCURER_ATTR,\n\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\tif (raw.fullyObscured) {\n\t\t\t\t\t\t\t\tawait new Promise((resolve) =>\n\t\t\t\t\t\t\t\t\tsetTimeout(resolve, OBSCURING_RECHECK_DELAY_MS),\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\traw = await adaptor.evaluate(\n\t\t\t\t\t\t\t\t\tmeasureObscuringScript,\n\t\t\t\t\t\t\t\t\tref,\n\t\t\t\t\t\t\t\t\tOBSCURER_ATTR,\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\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 =\n\t\t\t\t\t\t\t\t\tawait 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(\n\t\t\t\t\t\t\t\t\t\tgetSelector,\n\t\t\t\t\t\t\t\t\t\tobscurer,\n\t\t\t\t\t\t\t\t\t);\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"],"mappings":"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","names":["opacity","node","queryRoot","DEFAULT_SCREENSHOT_SETTLE_DELAY"]}
|
|
1
|
+
{"version":3,"sources":["../src/audit-scaffold.ts","../src/browser-scripts.ts","../src/clip.ts","../src/focus-style.ts","../src/get-selector.ts","../src/capture-screenshot.ts","../src/truncate-html.ts","../src/unfocused-pair.ts","../src/orchestrator.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 \"./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 * Scroll the element to the centre of the viewport, instantly so a screenshot taken immediately\n * afterwards isn't captured mid-animation.\n */\nexport function scrollToCenterScript(el: Element | null): void {\n\tel?.scrollIntoView({\n\t\tblock: \"center\",\n\t\tinline: \"nearest\",\n\t\tbehavior: \"instant\",\n\t});\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","import type { Rect } from \"./adaptor\";\n\n/**\n * A screenshot clip framing `rect` with up to `buffer` pixels of padding on each side, shrunk where\n * the page edges leave less room. The padding allows for indicators drawn just outside the element\n * box (outlines, shadows) and for anti-aliasing.\n */\nexport function bufferedClip(\n\trect: Rect,\n\tpageWidth: number,\n\tpageHeight: number,\n\tbuffer: number,\n): Rect {\n\tconst left = Math.max(0, Math.min(buffer, rect.x));\n\tconst top = Math.max(0, Math.min(buffer, rect.y));\n\tconst right = Math.max(0, Math.min(buffer, pageWidth - rect.x - rect.width));\n\tconst bottom = Math.max(0, Math.min(buffer, pageHeight - rect.y - rect.height));\n\n\t// Clamp to non-negative origins: an element positioned partially off the top or left of the\n\t// document has a negative page-relative rect, and Puppeteer rejects a screenshot clip with\n\t// negative x/y.\n\treturn {\n\t\tx: Math.max(0, rect.x - left),\n\t\ty: Math.max(0, rect.y - top),\n\t\twidth: rect.width + left + right,\n\t\theight: rect.height + top + bottom,\n\t};\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","/**\n * Build a compact, hopefully-unique selector for any given element on the page. This function is\n * serialized and run in the page context, so it must not reference any symbols outside its own\n * scope.\n *\n * Mirrors axe-core's approach: each level contributes the fewest features that discriminate the\n * element (a unique id, the rarest single class, or an :nth-of-type position), and the walk stops\n * as soon as the joined selector matches exactly one element. Selectors stay short because of that\n * early stop, not because of truncation; the length budget is only a backstop.\n */\nexport function getSelector(root: Node | null): string {\n\tconst MAX_LENGTH = 128;\n\n\tconst esc = (value: string): string =>\n\t\ttypeof CSS !== \"undefined\" && typeof CSS.escape === \"function\" ? CSS.escape(value) : value;\n\n\tconst tagName = (node: Node): string =>\n\t\tnode.nodeType === 1\n\t\t\t? node.nodeName.toLowerCase()\n\t\t\t: node.nodeName.toUpperCase().replace(/^#/, \"\");\n\n\t// A querySelectorAll-capable root (Document or ShadowRoot) for the element, used to confirm\n\t// uniqueness and class rarity. Absent on the plain objects used in the serialisation unit tests,\n\t// where we trust ids and walk to the top instead of stopping early.\n\tconst rootOf = (element: Element): ParentNode | null => {\n\t\tconst node = (element as Element & { getRootNode?: () => Node }).getRootNode?.();\n\n\t\treturn node && typeof (node as ParentNode).querySelectorAll === \"function\"\n\t\t\t? (node as ParentNode)\n\t\t\t: null;\n\t};\n\n\tconst matchCount = (queryRoot: ParentNode, selector: string): number => {\n\t\ttry {\n\t\t\treturn queryRoot.querySelectorAll(selector).length;\n\t\t} catch {\n\t\t\t// A selector that fails to parse can't be relied on.\n\t\t\treturn Number.POSITIVE_INFINITY;\n\t\t}\n\t};\n\n\t// A candidate feature is only usable if it actually selects this element; an escaping or engine\n\t// mismatch (e.g. CSS string escapes in an attribute value) could otherwise produce a selector\n\t// that matches nothing. Or a different element entirely.\n\tconst safeMatches = (element: Element, selector: string): boolean => {\n\t\ttry {\n\t\t\treturn typeof element.matches === \"function\" && element.matches(selector);\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t};\n\n\tconst idIsUnique = (element: Element, id: string): boolean => {\n\t\tconst queryRoot = rootOf(element);\n\n\t\treturn queryRoot === null || matchCount(queryRoot, `#${esc(id)}`) === 1;\n\t};\n\n\t// A discriminating feature for one level of the selector: its CSS text plus a predicate telling\n\t// whether a sibling would match the same feature (used to decide if an :nth-of-type pin is still\n\t// needed).\n\ttype Feature = {\n\t\tfeature: string;\n\t\tsharedBy: (sibling: Element) => boolean;\n\t};\n\n\tconst classFeature = (cls: string): Feature => ({\n\t\tfeature: `.${esc(cls)}`,\n\t\tsharedBy: (sibling) => Array.from(sibling.classList ?? []).includes(cls),\n\t});\n\n\t// The single feature that narrows the element down the most: the class or href attribute with the\n\t// fewest matches in the root. Hrefs break ties between structurally-identical content blocks,\n\t// where classes and :nth-of-type (which only pins position within one parent) cannot. Without a\n\t// queryable root there is no rarity information, so the first class stands in.\n\tconst rarestFeature = (element: Element, queryRoot: ParentNode | null): Feature | null => {\n\t\tconst classes = Array.from(element.classList).slice(0, 8);\n\n\t\tif (!queryRoot) {\n\t\t\tconst cls = classes[0];\n\n\t\t\treturn cls === undefined ? null : classFeature(cls);\n\t\t}\n\n\t\tlet best: Feature | null = null;\n\t\tlet bestCount = Number.POSITIVE_INFINITY;\n\n\t\tfor (const cls of classes) {\n\t\t\tconst candidate = classFeature(cls);\n\n\t\t\tif (!safeMatches(element, candidate.feature)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst count = matchCount(queryRoot, candidate.feature);\n\n\t\t\tif (count < bestCount) {\n\t\t\t\tbest = candidate;\n\t\t\t\tbestCount = count;\n\t\t\t}\n\n\t\t\tif (count === 1) {\n\t\t\t\treturn best;\n\t\t\t}\n\t\t}\n\n\t\tconst href = typeof element.getAttribute === \"function\" ? element.getAttribute(\"href\") : null;\n\n\t\t// Skip hrefs with characters that change meaning inside a CSS string (quotes, backslashes), and\n\t\t// query strings, which are too volatile across runs to pin a selector to. Fragments (#main)\n\t\t// stay eligible. Skip links make stable discriminators.\n\t\tif (href && href.length <= 100 && !/[\"\\\\?]/.test(href)) {\n\t\t\tconst feature = `[href=\"${href}\"]`;\n\n\t\t\tif (safeMatches(element, feature) && matchCount(queryRoot, feature) < bestCount) {\n\t\t\t\treturn {\n\t\t\t\t\tfeature,\n\t\t\t\t\tsharedBy: (sibling) =>\n\t\t\t\t\t\ttypeof sibling.getAttribute === \"function\" && sibling.getAttribute(\"href\") === href,\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\treturn best;\n\t};\n\n\t// The element's 1-based position among same-tag siblings, and whether the chosen feature fails to\n\t// discriminate it from them (so the segment needs an :nth-of-type pin). Siblings are absent on\n\t// the serialisation test nodes. There we skip the pin.\n\tconst positionAmongSiblings = (\n\t\telement: Element,\n\t\tsharedBy: (sibling: Element) => boolean,\n\t): { nth: number; ambiguous: boolean } => {\n\t\tconst siblings = (element.parentNode as ParentNode | null)?.children;\n\n\t\tif (!siblings) {\n\t\t\treturn { nth: 0, ambiguous: false };\n\t\t}\n\n\t\tlet nth = 0;\n\t\tlet count = 0;\n\t\tlet shared = 0;\n\n\t\tfor (const sibling of Array.from(siblings)) {\n\t\t\tif (sibling.nodeName !== element.nodeName) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tcount++;\n\n\t\t\tif (sibling === element) {\n\t\t\t\tnth = count;\n\t\t\t}\n\n\t\t\tif (sharedBy(sibling)) {\n\t\t\t\tshared++;\n\t\t\t}\n\t\t}\n\n\t\treturn { nth, ambiguous: shared > 1 };\n\t};\n\n\tconst segmentFor = (\n\t\telement: Element,\n\t\tqueryRoot: ParentNode | null,\n\t): { segment: string; anchored: boolean } => {\n\t\t// Only treat an id as an absolute anchor when it actually resolves to a single element: pages\n\t\t// with duplicate ids would otherwise yield a selector (e.g. \"#dup>a\") that matches several\n\t\t// elements.\n\t\tif (element.id && idIsUnique(element, element.id)) {\n\t\t\treturn { segment: `#${esc(element.id)}`, anchored: true };\n\t\t}\n\n\t\tconst feature = rarestFeature(element, queryRoot);\n\t\tlet segment = tagName(element) + (feature === null ? \"\" : feature.feature);\n\n\t\t// When same-tag siblings share the chosen feature, pin the position so the segment selects\n\t\t// exactly this element rather than all of them.\n\t\tconst { nth, ambiguous } = positionAmongSiblings(\n\t\t\telement,\n\t\t\tfeature === null ? () => true : feature.sharedBy,\n\t\t);\n\n\t\tif (ambiguous && nth > 0) {\n\t\t\tsegment += `:nth-of-type(${nth})`;\n\t\t}\n\n\t\treturn { segment, anchored: false };\n\t};\n\n\t// A minimal segment for when a level's full segment would blow the budget (e.g. an enormous class\n\t// name or a long href): just the tag, pinned by position when same-tag siblings would make the\n\t// bare tag ambiguous.\n\tconst degradedSegmentFor = (element: Element): string => {\n\t\tlet segment = tagName(element);\n\n\t\tconst { nth, ambiguous } = positionAmongSiblings(element, () => true);\n\n\t\tif (ambiguous && nth > 0) {\n\t\t\tsegment += `:nth-of-type(${nth})`;\n\t\t}\n\n\t\treturn segment;\n\t};\n\n\tconst segments: string[] = [];\n\tlet node: Node | null = root;\n\tconst queryRoot = root && root.nodeType === 1 ? rootOf(root as Element) : null;\n\n\ttry {\n\t\t// Walk only element nodes. Stopping at the first non-element parent (the document, or a\n\t\t// ShadowRoot when the target is inside an open shadow root) keeps the selector within its tree\n\t\t// and avoids reading `classList` off a node that has none.\n\t\twhile (node && node.nodeType === 1) {\n\t\t\tconst element = node as Element;\n\t\t\tlet { segment, anchored } = segmentFor(element, queryRoot);\n\n\t\t\t// Keep within the budget (the +1 accounts for the \">\" that would join this segment to the\n\t\t\t// rest). A level whose full segment doesn't fit is degraded to tag + position rather than\n\t\t\t// discarded, so ancestor context is never lost to one long feature.\n\t\t\tconst joinedWith = (candidate: string): number =>\n\t\t\t\tsegments.length ? candidate.length + 1 + segments.join(\">\").length : candidate.length;\n\n\t\t\tif (joinedWith(segment) > MAX_LENGTH - 1) {\n\t\t\t\tsegment = degradedSegmentFor(element);\n\t\t\t\tanchored = false;\n\n\t\t\t\tif (joinedWith(segment) > MAX_LENGTH - 1) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tsegments.unshift(segment);\n\n\t\t\tif (anchored) {\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t// Stop as soon as the selector already identifies a single element. This, not truncation, is\n\t\t\t// what keeps selectors short.\n\t\t\tif (queryRoot && matchCount(queryRoot, segments.join(\">\")) === 1) {\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tnode = element.parentNode;\n\t\t}\n\t} catch {\n\t\t// A malformed node tree must never crash the audit; return what we have.\n\t}\n\n\treturn segments.join(\">\");\n}\n","import type { BrowserAdaptor, ElementRef } from \"./adaptor\";\nimport { elementRectScript, pageDimensionsScript } from \"./browser-scripts\";\nimport { bufferedClip } from \"./clip\";\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","const DEFAULT_MAX_ATTRIBUTE_VALUE_LENGTH = 50;\nconst DEFAULT_MAX_LENGTH = 300;\nconst ELLIPSIS = \"...\";\n\nexport type TruncateHtmlOptions = {\n\tmaxAttributeValueLength?: number;\n\tmaxLength?: number;\n};\n\n/**\n * Given an element's outerHTML, produce a truncated version suitable for display in results. For\n * memory efficiency, pass a shallow outerHTML i.e. `Node.cloneNode(false).outerHTML`.\n */\nexport function truncateHtml(html: string, options: TruncateHtmlOptions = {}): string {\n\tif (!html) {\n\t\treturn \"\";\n\t}\n\n\tconst maxAttributeValueLength =\n\t\toptions.maxAttributeValueLength ?? DEFAULT_MAX_ATTRIBUTE_VALUE_LENGTH;\n\tconst maxLength = options.maxLength ?? DEFAULT_MAX_LENGTH;\n\n\t// Keep only the opening tag\n\tconst closingBracket = html.indexOf(\">\");\n\tconst openingTag = closingBracket === -1 ? html : html.slice(0, closingBracket + 1);\n\n\t// Shorten long quoted attribute values, preserving the quote style.\n\tconst trimmed = openingTag.replace(\n\t\t/=([\"'])([\\s\\S]*?)\\1/g,\n\t\t(match, quote: string, value: string) => {\n\t\t\tif (value.length <= maxAttributeValueLength) {\n\t\t\t\treturn match;\n\t\t\t}\n\n\t\t\treturn `=${quote}${value.slice(0, maxAttributeValueLength)}${ELLIPSIS}${quote}`;\n\t\t},\n\t);\n\n\tif (trimmed.length > maxLength) {\n\t\treturn `${trimmed.slice(0, maxLength)}${ELLIPSIS}`;\n\t}\n\n\treturn trimmed;\n}\n","import type { BrowserAdaptor, ElementRef } from \"./adaptor\";\nimport {\n\tblurScript,\n\telementRectScript,\n\telementStylesScript,\n\tfocusScript,\n\tisCenterObscuredScript,\n\tpageDimensionsScript,\n\tscrollToCenterScript,\n} from \"./browser-scripts\";\nimport { bufferedClip } from \"./clip\";\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. Centre the element\n\t// in the viewport first, then give the scroll a moment to settle.\n\tif (await adaptor.evaluate(isCenterObscuredScript, handle)) {\n\t\tawait adaptor.evaluate(scrollToCenterScript, handle);\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","import type { BrowserAdaptor, ElementRef } from \"./adaptor\";\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 { getSelector } from \"./get-selector\";\nimport { truncateHtml } from \"./truncate-html\";\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 */\nfunction isContextDestroyedError(error: unknown): boolean {\n\tconst message = error instanceof Error ? error.message : String(error);\n\n\treturn /Execution context was destroyed|Target closed|frame was detached|navigat/i.test(message);\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"],"mappings":"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","names":["opacity","node","queryRoot","DEFAULT_SCREENSHOT_SETTLE_DELAY"]}
|
package/dist/types.d.ts
CHANGED
|
@@ -57,9 +57,9 @@ export type TabSessionHandle = {
|
|
|
57
57
|
disconnect(): void;
|
|
58
58
|
ensureUnfocusedPair(): Promise<UnfocusedPair>;
|
|
59
59
|
/**
|
|
60
|
-
* Screenshot the currently focused element, clipped and padded. Requires the
|
|
61
|
-
*
|
|
62
|
-
*
|
|
60
|
+
* Screenshot the currently focused element, clipped and padded. Requires the `screenshot`
|
|
61
|
+
* capability. Cached per stop, so multiple consumers calling this on the same stop only trigger
|
|
62
|
+
* one capture.
|
|
63
63
|
*/
|
|
64
64
|
screenshotClip(): Promise<Uint8Array>;
|
|
65
65
|
};
|
package/dist/unfocused-pair.d.ts
CHANGED
|
@@ -6,8 +6,8 @@ export type CaptureUnfocusedPairOptions = {
|
|
|
6
6
|
styleProps: readonly string[];
|
|
7
7
|
};
|
|
8
8
|
/**
|
|
9
|
-
* Capture focused and unfocused screenshots of the same element, plus the
|
|
10
|
-
*
|
|
9
|
+
* Capture focused and unfocused screenshots of the same element, plus the unfocused style snapshot
|
|
10
|
+
* and clip anchors appearance needs to score later.
|
|
11
11
|
*
|
|
12
12
|
* The caller owns `handle` and must dispose it after this promise settles.
|
|
13
13
|
*/
|