browser-debugger-cli 0.12.0 → 0.14.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/.claude/skills/bdg/SKILL.md +100 -186
- package/README.md +5 -4
- package/dist/commands/cdp.d.ts +22 -1
- package/dist/commands/cdp.js +100 -43
- package/dist/commands/console.d.ts +12 -0
- package/dist/commands/console.js +67 -13
- package/dist/commands/dom/DomElementResolver.d.ts +3 -1
- package/dist/commands/dom/DomElementResolver.js +10 -3
- package/dist/commands/dom/a11y.d.ts +1 -1
- package/dist/commands/dom/a11y.js +23 -22
- package/dist/commands/dom/eval.d.ts +4 -2
- package/dist/commands/dom/eval.js +31 -7
- package/dist/commands/dom/form.js +10 -9
- package/dist/commands/dom/formInteraction.js +9 -8
- package/dist/commands/dom/get.js +32 -14
- package/dist/commands/dom/helpers/index.d.ts +1 -1
- package/dist/commands/dom/helpers/index.js +1 -1
- package/dist/commands/dom/helpers/query.d.ts +27 -3
- package/dist/commands/dom/helpers/query.js +152 -64
- package/dist/commands/dom/helpers/screenshot.js +13 -13
- package/dist/commands/dom/index.js +10 -3
- package/dist/commands/dom/query.d.ts +20 -2
- package/dist/commands/dom/query.js +39 -6
- package/dist/commands/dom/screenshot.js +3 -1
- package/dist/commands/dom/semanticUtils.d.ts +3 -2
- package/dist/commands/dom/semanticUtils.js +40 -9
- package/dist/commands/helpJson.d.ts +82 -19
- package/dist/commands/helpJson.js +112 -41
- package/dist/commands/helpTopic.d.ts +16 -1
- package/dist/commands/helpTopic.js +59 -1
- package/dist/commands/installSkill.d.ts +15 -5
- package/dist/commands/installSkill.js +86 -16
- package/dist/commands/network/list.js +65 -12
- package/dist/commands/optionBehaviors.d.ts +25 -2
- package/dist/commands/optionBehaviors.js +81 -46
- package/dist/commands/peek.js +3 -0
- package/dist/commands/shared/CommandRunner.js +13 -13
- package/dist/commands/shared/daemonErrorHandler.d.ts +5 -2
- package/dist/commands/shared/daemonErrorHandler.js +21 -10
- package/dist/commands/shared/dataFetcher.d.ts +14 -4
- package/dist/commands/shared/dataFetcher.js +20 -4
- package/dist/commands/shared/followMode.d.ts +9 -1
- package/dist/commands/shared/followMode.js +22 -4
- package/dist/commands/shared/handleValidationError.js +3 -3
- package/dist/commands/shared/optionTypes.d.ts +17 -3
- package/dist/commands/shared/outputFile.js +6 -1
- package/dist/commands/shared/startHelpers.js +3 -3
- package/dist/commands/start.d.ts +7 -5
- package/dist/commands/start.js +65 -21
- package/dist/commands/stop.d.ts +11 -0
- package/dist/commands/stop.js +24 -1
- package/dist/commands.js +1 -1
- package/dist/connection/cdp.d.ts +7 -0
- package/dist/connection/cdp.js +9 -0
- package/dist/connection/chromeIdentity.d.ts +8 -2
- package/dist/connection/chromeIdentity.js +85 -13
- package/dist/connection/launcher.js +3 -2
- package/dist/constants.d.ts +29 -1
- package/dist/constants.js +35 -1
- package/dist/daemon/SessionController.js +8 -1
- package/dist/daemon/launcher.d.ts +3 -2
- package/dist/daemon/launcher.js +47 -3
- package/dist/daemon/session/Session.d.ts +5 -1
- package/dist/daemon/session/Session.js +42 -3
- package/dist/daemon/session/TelemetryStore.d.ts +15 -1
- package/dist/daemon/session/TelemetryStore.js +19 -1
- package/dist/daemon/session/commandRegistry.js +52 -18
- package/dist/daemon/session/interactions.d.ts +2 -1
- package/dist/daemon/session/interactions.js +13 -1
- package/dist/daemon/session/matchedStylesReset.d.ts +26 -0
- package/dist/daemon/session/matchedStylesReset.js +46 -0
- package/dist/daemon/session/plugins.js +17 -2
- package/dist/daemon/session/teardown.js +1 -1
- package/dist/daemon/session/triggeredRequests.d.ts +0 -5
- package/dist/daemon/session/triggeredRequests.js +13 -7
- package/dist/daemon.js +2385 -1229
- package/dist/errors/messages.d.ts +62 -11
- package/dist/errors/messages.js +119 -22
- package/dist/index.js +14995 -9866
- package/dist/ipc/client.d.ts +18 -2
- package/dist/ipc/client.js +26 -5
- package/dist/ipc/protocol/auditTypes.d.ts +8 -2
- package/dist/ipc/protocol/commands.d.ts +16 -0
- package/dist/ipc/protocol/domTypes.d.ts +12 -0
- package/dist/ipc/protocol/inspectTypes.d.ts +7 -2
- package/dist/ipc/session/types.d.ts +7 -1
- package/dist/program.d.ts +14 -0
- package/dist/program.js +53 -0
- package/dist/runtime/dom/actionEffects.d.ts +5 -1
- package/dist/runtime/dom/actionEffects.js +26 -14
- package/dist/runtime/dom/audit.js +3 -2
- package/dist/runtime/dom/auditModel.js +6 -1
- package/dist/runtime/dom/auditScripts.d.ts +9 -3
- package/dist/runtime/dom/auditScripts.js +41 -5
- package/dist/runtime/dom/elementGeometry.d.ts +33 -3
- package/dist/runtime/dom/elementGeometry.js +44 -19
- package/dist/runtime/dom/elementInfo.d.ts +76 -18
- package/dist/runtime/dom/elementInfo.js +190 -40
- package/dist/runtime/dom/evalHelpers.d.ts +12 -2
- package/dist/runtime/dom/evalHelpers.js +67 -7
- package/dist/runtime/dom/formDiscovery.d.ts +6 -2
- package/dist/runtime/dom/formDiscovery.js +20 -3
- package/dist/runtime/dom/formFillHelpers/fill.js +7 -11
- package/dist/runtime/dom/formFillHelpers/pressKey.js +2 -2
- package/dist/runtime/dom/formFillHelpers/shared.d.ts +16 -10
- package/dist/runtime/dom/formFillHelpers/shared.js +19 -52
- package/dist/runtime/dom/formSubmitHelpers.js +4 -3
- package/dist/runtime/dom/frameLayout.js +1 -0
- package/dist/runtime/dom/frameScopedConnection.d.ts +7 -0
- package/dist/runtime/dom/frameScopedConnection.js +2 -2
- package/dist/runtime/dom/inspect.d.ts +17 -3
- package/dist/runtime/dom/inspect.js +45 -32
- package/dist/runtime/dom/inspectAllStyles.js +1 -0
- package/dist/runtime/dom/inspectHints.d.ts +1 -1
- package/dist/runtime/dom/inspectModel.d.ts +5 -4
- package/dist/runtime/dom/inspectModel.js +7 -3
- package/dist/runtime/dom/inspectPaintModel.d.ts +2 -0
- package/dist/runtime/dom/inspectPaintModel.js +3 -1
- package/dist/runtime/dom/inspectRules.d.ts +29 -3
- package/dist/runtime/dom/inspectRules.js +205 -11
- package/dist/runtime/dom/inspectScripts.d.ts +29 -2
- package/dist/runtime/dom/inspectScripts.js +49 -10
- package/dist/runtime/dom/layout.d.ts +0 -2
- package/dist/runtime/dom/layout.js +10 -9
- package/dist/runtime/dom/reactEventHelpers.d.ts +17 -4
- package/dist/runtime/dom/reactEventHelpers.js +71 -28
- package/dist/runtime/dom/targetNode.d.ts +27 -10
- package/dist/runtime/dom/targetNode.js +283 -16
- package/dist/runtime/dom/wait.js +2 -1
- package/dist/runtime/page/bdgWorld.d.ts +57 -0
- package/dist/runtime/page/bdgWorld.js +180 -0
- package/dist/runtime/page/replacedBuiltins.d.ts +28 -0
- package/dist/runtime/page/replacedBuiltins.js +136 -0
- package/dist/session/QueryCacheManager.d.ts +4 -1
- package/dist/session/QueryCacheManager.js +5 -2
- package/dist/session/chrome.d.ts +4 -1
- package/dist/session/chrome.js +7 -1
- package/dist/session/cleanup/staleSession.d.ts +21 -4
- package/dist/session/cleanup/staleSession.js +79 -9
- package/dist/session/cleanup/userCommands.d.ts +4 -1
- package/dist/session/cleanup/userCommands.js +10 -5
- package/dist/session/daemonSocket.d.ts +10 -0
- package/dist/session/daemonSocket.js +22 -0
- package/dist/session/lastSession.d.ts +6 -3
- package/dist/session/lastSession.js +11 -5
- package/dist/session/paths.d.ts +3 -1
- package/dist/session/paths.js +5 -5
- package/dist/session/portClaims.js +4 -3
- package/dist/session/sessionList.d.ts +13 -5
- package/dist/session/sessionList.js +31 -7
- package/dist/telemetry/a11y.d.ts +15 -1
- package/dist/telemetry/a11y.js +85 -2
- package/dist/telemetry/console.d.ts +2 -1
- package/dist/telemetry/console.js +30 -21
- package/dist/telemetry/har/builder.js +1 -1
- package/dist/telemetry/network.d.ts +13 -16
- package/dist/telemetry/network.js +30 -52
- package/dist/telemetry/networkRetention.d.ts +83 -0
- package/dist/telemetry/networkRetention.js +117 -0
- package/dist/telemetry/pageCrash.d.ts +26 -0
- package/dist/telemetry/pageCrash.js +53 -0
- package/dist/types.d.ts +42 -0
- package/dist/ui/OutputBuilder.d.ts +10 -0
- package/dist/ui/OutputBuilder.js +12 -0
- package/dist/ui/formatters/a11y.d.ts +5 -7
- package/dist/ui/formatters/a11y.js +7 -61
- package/dist/ui/formatters/audit.js +14 -5
- package/dist/ui/formatters/cdp.d.ts +138 -0
- package/dist/ui/formatters/cdp.js +131 -0
- package/dist/ui/formatters/console/chronological.js +7 -5
- package/dist/ui/formatters/console/follow.d.ts +5 -2
- package/dist/ui/formatters/console/follow.js +7 -4
- package/dist/ui/formatters/console/json.d.ts +4 -7
- package/dist/ui/formatters/console/json.js +16 -14
- package/dist/ui/formatters/console/shared.d.ts +47 -2
- package/dist/ui/formatters/console/shared.js +33 -0
- package/dist/ui/formatters/console/summarize.d.ts +9 -2
- package/dist/ui/formatters/console/summarize.js +57 -11
- package/dist/ui/formatters/console.d.ts +3 -2
- package/dist/ui/formatters/console.js +8 -10
- package/dist/ui/formatters/details.js +4 -2
- package/dist/ui/formatters/dom.d.ts +14 -5
- package/dist/ui/formatters/dom.js +30 -13
- package/dist/ui/formatters/helpFormatters.js +1 -1
- package/dist/ui/formatters/inspect.js +9 -3
- package/dist/ui/formatters/installSkill.d.ts +9 -1
- package/dist/ui/formatters/installSkill.js +32 -6
- package/dist/ui/formatters/layout.js +4 -2
- package/dist/ui/formatters/longValues.d.ts +14 -0
- package/dist/ui/formatters/longValues.js +23 -0
- package/dist/ui/formatters/networkList.d.ts +8 -2
- package/dist/ui/formatters/networkList.js +11 -3
- package/dist/ui/formatters/preview.d.ts +6 -1
- package/dist/ui/formatters/preview.js +67 -15
- package/dist/ui/formatters/sessions.d.ts +2 -2
- package/dist/ui/formatters/sessions.js +9 -2
- package/dist/ui/formatters/status.js +7 -0
- package/dist/ui/formatters/triggeredRequests.js +2 -1
- package/dist/ui/logging/logger.d.ts +1 -1
- package/dist/ui/messages/chrome.d.ts +20 -1
- package/dist/ui/messages/chrome.js +29 -3
- package/dist/ui/messages/commands.d.ts +153 -12
- package/dist/ui/messages/commands.js +198 -15
- package/dist/ui/messages/consoleMessages.d.ts +24 -0
- package/dist/ui/messages/consoleMessages.js +32 -0
- package/dist/ui/messages/networkMessages.d.ts +24 -0
- package/dist/ui/messages/networkMessages.js +45 -0
- package/dist/ui/messages/preview.d.ts +6 -0
- package/dist/ui/messages/preview.js +9 -1
- package/dist/ui/messages/session.d.ts +13 -2
- package/dist/ui/messages/session.js +22 -3
- package/dist/utils/directories.d.ts +34 -0
- package/dist/utils/directories.js +88 -0
- package/dist/utils/display.d.ts +16 -0
- package/dist/utils/display.js +42 -0
- package/dist/utils/exitCodes.d.ts +1 -0
- package/dist/utils/exitCodes.js +6 -0
- package/dist/utils/http.d.ts +9 -2
- package/dist/utils/http.js +4 -3
- package/dist/utils/process.d.ts +12 -0
- package/dist/utils/process.js +25 -0
- package/dist/utils/strings.d.ts +19 -0
- package/dist/utils/strings.js +16 -0
- package/package.json +2 -2
package/dist/ipc/client.d.ts
CHANGED
|
@@ -178,8 +178,10 @@ export declare function getNetworkHeaders(options?: {
|
|
|
178
178
|
*
|
|
179
179
|
* @param method - CDP method name (e.g., 'Network.getCookies')
|
|
180
180
|
* @param params - Optional method parameters
|
|
181
|
+
* @param options - `isolated`: a bdg page script, run in bdg's isolated world
|
|
182
|
+
* (`Runtime.evaluate`, `DOM.resolveNode`)
|
|
181
183
|
* @returns Response with CDP method result
|
|
182
|
-
* @throws Error if connection fails; CommandError (102) when the page was busy and its scripts were terminated
|
|
184
|
+
* @throws Error if connection fails; CommandError (102) when the page was busy and its scripts were terminated, (107) when the page crashed
|
|
183
185
|
*
|
|
184
186
|
* @example
|
|
185
187
|
* ```typescript
|
|
@@ -189,7 +191,21 @@ export declare function getNetworkHeaders(options?: {
|
|
|
189
191
|
* }
|
|
190
192
|
* ```
|
|
191
193
|
*/
|
|
192
|
-
export declare function callCDP(method: string, params?: Record<string, unknown
|
|
194
|
+
export declare function callCDP(method: string, params?: Record<string, unknown>, options?: {
|
|
195
|
+
isolated?: boolean;
|
|
196
|
+
}): Promise<ClientResponse<'cdp_call'>>;
|
|
197
|
+
/**
|
|
198
|
+
* Send a CDP call for one of bdg's own page scripts: `Runtime.evaluate` and
|
|
199
|
+
* `DOM.resolveNode` run in bdg's isolated world, so a page that replaced
|
|
200
|
+
* built-ins (`querySelectorAll`, `JSON.stringify`) cannot change what they
|
|
201
|
+
* find or return.
|
|
202
|
+
*
|
|
203
|
+
* @param method - CDP method name
|
|
204
|
+
* @param params - Method parameters
|
|
205
|
+
* @returns Response with the CDP method result
|
|
206
|
+
* @throws Like {@link callCDP}
|
|
207
|
+
*/
|
|
208
|
+
export declare function callBdgScript(method: string, params?: Record<string, unknown>): Promise<ClientResponse<'cdp_call'>>;
|
|
193
209
|
/**
|
|
194
210
|
* Evaluate a JavaScript expression in the active page (or one of its iframes) via the daemon.
|
|
195
211
|
*/
|
package/dist/ipc/client.js
CHANGED
|
@@ -251,8 +251,10 @@ export async function getNetworkHeaders(options) {
|
|
|
251
251
|
*
|
|
252
252
|
* @param method - CDP method name (e.g., 'Network.getCookies')
|
|
253
253
|
* @param params - Optional method parameters
|
|
254
|
+
* @param options - `isolated`: a bdg page script, run in bdg's isolated world
|
|
255
|
+
* (`Runtime.evaluate`, `DOM.resolveNode`)
|
|
254
256
|
* @returns Response with CDP method result
|
|
255
|
-
* @throws Error if connection fails; CommandError (102) when the page was busy and its scripts were terminated
|
|
257
|
+
* @throws Error if connection fails; CommandError (102) when the page was busy and its scripts were terminated, (107) when the page crashed
|
|
256
258
|
*
|
|
257
259
|
* @example
|
|
258
260
|
* ```typescript
|
|
@@ -262,13 +264,32 @@ export async function getNetworkHeaders(options) {
|
|
|
262
264
|
* }
|
|
263
265
|
* ```
|
|
264
266
|
*/
|
|
265
|
-
export async function callCDP(method, params) {
|
|
266
|
-
const response = await sendCommand('cdp_call', {
|
|
267
|
-
|
|
268
|
-
|
|
267
|
+
export async function callCDP(method, params, options = {}) {
|
|
268
|
+
const response = await sendCommand('cdp_call', {
|
|
269
|
+
method,
|
|
270
|
+
...(params && { params }),
|
|
271
|
+
...(options.isolated && { isolated: true }),
|
|
272
|
+
});
|
|
273
|
+
const fatal = [EXIT_CODES.CDP_TIMEOUT, EXIT_CODES.PAGE_CRASHED];
|
|
274
|
+
if (response.status === 'error' && response.exitCode && fatal.includes(response.exitCode)) {
|
|
275
|
+
throw new CommandError(response.error ?? `${method} failed`, response.suggestion ? { suggestion: response.suggestion } : {}, response.exitCode);
|
|
269
276
|
}
|
|
270
277
|
return response;
|
|
271
278
|
}
|
|
279
|
+
/**
|
|
280
|
+
* Send a CDP call for one of bdg's own page scripts: `Runtime.evaluate` and
|
|
281
|
+
* `DOM.resolveNode` run in bdg's isolated world, so a page that replaced
|
|
282
|
+
* built-ins (`querySelectorAll`, `JSON.stringify`) cannot change what they
|
|
283
|
+
* find or return.
|
|
284
|
+
*
|
|
285
|
+
* @param method - CDP method name
|
|
286
|
+
* @param params - Method parameters
|
|
287
|
+
* @returns Response with the CDP method result
|
|
288
|
+
* @throws Like {@link callCDP}
|
|
289
|
+
*/
|
|
290
|
+
export function callBdgScript(method, params) {
|
|
291
|
+
return callCDP(method, params, { isolated: true });
|
|
292
|
+
}
|
|
272
293
|
/**
|
|
273
294
|
* Evaluate a JavaScript expression in the active page (or one of its iframes) via the daemon.
|
|
274
295
|
*/
|
|
@@ -19,7 +19,9 @@ export interface AuditContrastItem {
|
|
|
19
19
|
size: number;
|
|
20
20
|
weight: number;
|
|
21
21
|
inView: boolean;
|
|
22
|
-
/**
|
|
22
|
+
/** Opacity of the text and its ancestors, when below 1 (the colors are faded by it) */
|
|
23
|
+
opacity?: number;
|
|
24
|
+
/** Why the ratio is approximate (blend modes, filters, an element behind or on top, out of view) */
|
|
23
25
|
approximate?: string[];
|
|
24
26
|
}
|
|
25
27
|
/** An image drawn larger than its pixels, or with another aspect ratio */
|
|
@@ -51,8 +53,10 @@ export interface AuditResult {
|
|
|
51
53
|
level: 'AA' | 'AAA';
|
|
52
54
|
/** Text holders checked */
|
|
53
55
|
checked: number;
|
|
54
|
-
/** How many are below the level */
|
|
56
|
+
/** How many are below the level (measured exactly) */
|
|
55
57
|
failing: number;
|
|
58
|
+
/** How many more look below the level but cannot be measured exactly (text over images, blend modes, layers): not listed, `dom inspect` them */
|
|
59
|
+
uncertain?: number;
|
|
56
60
|
/** The weakest ones, at most `--limit` */
|
|
57
61
|
items: AuditContrastItem[];
|
|
58
62
|
};
|
|
@@ -109,6 +113,8 @@ export interface AuditResult {
|
|
|
109
113
|
/** Identical animations this one stands for (2 or more) */
|
|
110
114
|
count?: number;
|
|
111
115
|
}>;
|
|
116
|
+
/** Visible `<canvas>` elements: scripts may animate them, which `animations` cannot see */
|
|
117
|
+
canvases?: number;
|
|
112
118
|
}
|
|
113
119
|
/** A stylesheet line where `css search` found the text */
|
|
114
120
|
export interface CssSearchMatch {
|
|
@@ -66,10 +66,18 @@ export interface SessionPeekData {
|
|
|
66
66
|
}>;
|
|
67
67
|
/** Navigation id of the page currently loaded. */
|
|
68
68
|
currentNavigationId: number;
|
|
69
|
+
/** When the page's renderer crashed (epoch ms), while it is not loaded again */
|
|
70
|
+
pageCrashedAt?: number;
|
|
69
71
|
/** Total number of network requests (for pagination). */
|
|
70
72
|
totalNetwork: number;
|
|
71
73
|
/** Total number of console messages (for pagination). */
|
|
72
74
|
totalConsole: number;
|
|
75
|
+
/** Console messages dropped at the limit (the oldest; indices start after them) */
|
|
76
|
+
droppedConsole?: number;
|
|
77
|
+
/** Finished network requests dropped at the request cap (the oldest) */
|
|
78
|
+
droppedNetwork?: number;
|
|
79
|
+
/** Response bodies evicted at the total body budget (the oldest) */
|
|
80
|
+
evictedNetworkBodies?: number;
|
|
73
81
|
/** Whether there are more network items available. */
|
|
74
82
|
hasMoreNetwork?: boolean;
|
|
75
83
|
/** Whether there are more console items available. */
|
|
@@ -99,6 +107,12 @@ export interface CdpCallCommand {
|
|
|
99
107
|
method: string;
|
|
100
108
|
/** Optional parameters for the CDP method. */
|
|
101
109
|
params?: Record<string, unknown>;
|
|
110
|
+
/**
|
|
111
|
+
* A bdg page script: `Runtime.evaluate` and `DOM.resolveNode` run in bdg's
|
|
112
|
+
* isolated world, out of reach of built-ins the page replaced (not for
|
|
113
|
+
* `bdg cdp`, whose calls stay in the page's world)
|
|
114
|
+
*/
|
|
115
|
+
isolated?: boolean;
|
|
102
116
|
}
|
|
103
117
|
/**
|
|
104
118
|
* CDP call command response data.
|
|
@@ -183,6 +197,8 @@ export interface DomEvalData {
|
|
|
183
197
|
subtype?: string;
|
|
184
198
|
/** URL of the iframe the script ran in (with `frame`; empty when it has none) */
|
|
185
199
|
frame?: string;
|
|
200
|
+
/** Set when the page replaced built-ins bdg's copy of the result uses */
|
|
201
|
+
warning?: string;
|
|
186
202
|
}
|
|
187
203
|
/** An iframe of the page, as listed by `bdg dom frames` */
|
|
188
204
|
export interface DomFrame {
|
|
@@ -96,6 +96,8 @@ export interface ActionEffects {
|
|
|
96
96
|
settled?: false;
|
|
97
97
|
/** What the page was still working on (with `settled: false`) */
|
|
98
98
|
pending?: PendingChanges;
|
|
99
|
+
/** All built-ins the page replaced that bdg's action scripts use (when the warning mentions them) */
|
|
100
|
+
replacedBuiltins?: string[];
|
|
99
101
|
}
|
|
100
102
|
/** A filled field's value differing from the one given */
|
|
101
103
|
export interface FillValueMismatch {
|
|
@@ -165,6 +167,11 @@ export interface ClickResult extends ActionEffects {
|
|
|
165
167
|
method?: 'mouse' | 'dom';
|
|
166
168
|
/** What was done: click, double (click), right (click) or hover */
|
|
167
169
|
action?: 'click' | 'double' | 'right' | 'hover';
|
|
170
|
+
/** How far the page scrolled to bring the element into view (CSS px; absent when it did not move) */
|
|
171
|
+
scrolledBy?: {
|
|
172
|
+
x: number;
|
|
173
|
+
y: number;
|
|
174
|
+
};
|
|
168
175
|
/** Exit code for a failure */
|
|
169
176
|
exitCode?: number;
|
|
170
177
|
/** Why the DOM fallback was used (element covered or without size) */
|
|
@@ -425,6 +432,11 @@ export interface ElementLayout {
|
|
|
425
432
|
* `opacity: 0 on div#menu` (`inViewport` still says where it is)
|
|
426
433
|
*/
|
|
427
434
|
invisible?: string;
|
|
435
|
+
/**
|
|
436
|
+
* A `mask-image` on it or an ancestor, e.g. `mask-image on div.hero`: part
|
|
437
|
+
* or all of it may not show (how much is not evaluated)
|
|
438
|
+
*/
|
|
439
|
+
masked?: string;
|
|
428
440
|
/** Inside an `inert` element: shown, but a user cannot interact with it */
|
|
429
441
|
inert?: true;
|
|
430
442
|
computed: LayoutComputedStyle;
|
|
@@ -270,6 +270,8 @@ export interface InspectVisibility {
|
|
|
270
270
|
coveredBy?: string;
|
|
271
271
|
/** The cover paints nothing there: the element shows, but clicks land on the cover */
|
|
272
272
|
coverTransparent?: true;
|
|
273
|
+
/** A `mask-image` on it or an ancestor, e.g. `mask-image on div.hero` (how much it hides is not evaluated) */
|
|
274
|
+
masked?: string;
|
|
273
275
|
}
|
|
274
276
|
/** A property asked for with `--props` */
|
|
275
277
|
export interface InspectProp {
|
|
@@ -445,7 +447,10 @@ export interface InspectResult {
|
|
|
445
447
|
rules?: InspectRule[];
|
|
446
448
|
/** `--why <property>`: one entry, or one per longhand of a shorthand whose sides differ */
|
|
447
449
|
why?: InspectWhy[];
|
|
448
|
-
/**
|
|
449
|
-
|
|
450
|
+
/**
|
|
451
|
+
* The cascade was not read: Chrome took longer than the time allowed, failed,
|
|
452
|
+
* or (hints only) was not asked because it was too slow earlier on this page
|
|
453
|
+
*/
|
|
454
|
+
cascade?: 'timeout' | 'failed' | 'skipped';
|
|
450
455
|
}
|
|
451
456
|
//# sourceMappingURL=inspectTypes.d.ts.map
|
|
@@ -8,8 +8,12 @@ import type { ColorScheme, ViewportSize } from '../../types.js';
|
|
|
8
8
|
* Session activity metrics.
|
|
9
9
|
*/
|
|
10
10
|
export interface SessionActivity {
|
|
11
|
-
/**
|
|
11
|
+
/** Network requests kept (finished ones; the newest at the cap). */
|
|
12
12
|
networkRequestsCaptured: number;
|
|
13
|
+
/** Oldest finished requests dropped at the request cap (left out when none). */
|
|
14
|
+
networkRequestsDropped?: number;
|
|
15
|
+
/** Oldest response bodies evicted at the total body budget (left out when none). */
|
|
16
|
+
networkBodiesEvicted?: number;
|
|
13
17
|
/** Total console messages captured. */
|
|
14
18
|
consoleMessagesCaptured: number;
|
|
15
19
|
/** Timestamp of last network request. */
|
|
@@ -29,5 +33,7 @@ export interface PageState {
|
|
|
29
33
|
viewport?: ViewportSize;
|
|
30
34
|
/** `prefers-color-scheme` the page sees (left out when the page did not answer in time). */
|
|
31
35
|
colorScheme?: ColorScheme;
|
|
36
|
+
/** When the page's renderer crashed (epoch ms); `bdg page reload` brings it back */
|
|
37
|
+
crashedAt?: number;
|
|
32
38
|
}
|
|
33
39
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The bdg command tree: root command, its options and every registered command.
|
|
3
|
+
*/
|
|
4
|
+
import { Command, type OutputConfiguration } from 'commander';
|
|
5
|
+
/**
|
|
6
|
+
* Build the bdg program with every command registered, without parsing
|
|
7
|
+
* anything. Commander errors throw instead of exiting; the output
|
|
8
|
+
* configuration is set before commands are registered so they inherit it.
|
|
9
|
+
*
|
|
10
|
+
* @param output - Where Commander writes help and errors
|
|
11
|
+
* @returns Root command
|
|
12
|
+
*/
|
|
13
|
+
export declare function buildProgram(output?: OutputConfiguration): Command;
|
|
14
|
+
//# sourceMappingURL=program.d.ts.map
|
package/dist/program.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The bdg command tree: root command, its options and every registered command.
|
|
3
|
+
*/
|
|
4
|
+
import { Command, Option } from 'commander';
|
|
5
|
+
import { commandRegistry } from './commands.js';
|
|
6
|
+
import { VERSION } from './utils/version.js';
|
|
7
|
+
const CLI_NAME = 'bdg';
|
|
8
|
+
const CLI_DESCRIPTION = 'Browser telemetry via Chrome DevTools Protocol';
|
|
9
|
+
const SESSION_OPTION_FLAGS = '--session <name>';
|
|
10
|
+
const SESSION_OPTION_DESCRIPTION = 'Use a named session (own daemon, Chrome and port) instead of the default one; env: BDG_SESSION';
|
|
11
|
+
/**
|
|
12
|
+
* Make `--debug`, `-q` and `--session` accepted after any subcommand (program
|
|
13
|
+
* options are positional).
|
|
14
|
+
*
|
|
15
|
+
* @param command - Command whose subcommands get the hidden global options
|
|
16
|
+
*/
|
|
17
|
+
function addGlobalOptions(command) {
|
|
18
|
+
for (const sub of command.commands) {
|
|
19
|
+
if (!sub.options.some((option) => option.long === '--debug')) {
|
|
20
|
+
sub.addOption(new Option('--debug', 'Enable debug logging').hideHelp());
|
|
21
|
+
}
|
|
22
|
+
if (!sub.options.some((option) => option.long === '--quiet')) {
|
|
23
|
+
sub.addOption(new Option('-q, --quiet', 'Hide tips and hints').hideHelp());
|
|
24
|
+
}
|
|
25
|
+
if (!sub.options.some((option) => option.long === '--session')) {
|
|
26
|
+
sub.addOption(new Option(SESSION_OPTION_FLAGS, SESSION_OPTION_DESCRIPTION).hideHelp());
|
|
27
|
+
}
|
|
28
|
+
addGlobalOptions(sub);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Build the bdg program with every command registered, without parsing
|
|
33
|
+
* anything. Commander errors throw instead of exiting; the output
|
|
34
|
+
* configuration is set before commands are registered so they inherit it.
|
|
35
|
+
*
|
|
36
|
+
* @param output - Where Commander writes help and errors
|
|
37
|
+
* @returns Root command
|
|
38
|
+
*/
|
|
39
|
+
export function buildProgram(output = {}) {
|
|
40
|
+
const program = new Command()
|
|
41
|
+
.name(CLI_NAME)
|
|
42
|
+
.description(CLI_DESCRIPTION)
|
|
43
|
+
.version(VERSION)
|
|
44
|
+
.option('--debug', 'Enable debug logging (verbose output)')
|
|
45
|
+
.option(SESSION_OPTION_FLAGS, SESSION_OPTION_DESCRIPTION)
|
|
46
|
+
.enablePositionalOptions()
|
|
47
|
+
.exitOverride()
|
|
48
|
+
.configureOutput(output);
|
|
49
|
+
commandRegistry.forEach((register) => register(program));
|
|
50
|
+
addGlobalOptions(program);
|
|
51
|
+
return program;
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=program.js.map
|
|
@@ -65,6 +65,8 @@ export interface OtherActivity {
|
|
|
65
65
|
dialogs: number;
|
|
66
66
|
/** A window, tab or download was opened */
|
|
67
67
|
opened: boolean;
|
|
68
|
+
/** Console messages logged during the action */
|
|
69
|
+
consoleMessages: number;
|
|
68
70
|
}
|
|
69
71
|
/**
|
|
70
72
|
* Messages that are new after the action: all of them after a new document
|
|
@@ -147,7 +149,7 @@ export declare function pageNavigation(startHref: string | undefined, read: Read
|
|
|
147
149
|
* Whether an action had no visible effect: the page was read before and
|
|
148
150
|
* after in the same document, it counted no DOM change, nothing made the
|
|
149
151
|
* check uncertain, and no navigation, message, shown element, request,
|
|
150
|
-
* dialog
|
|
152
|
+
* dialog, new window or console message happened.
|
|
151
153
|
*
|
|
152
154
|
* @param read - Page read after the action
|
|
153
155
|
* @param effects - Navigation and messages found
|
|
@@ -159,6 +161,8 @@ export declare function hadNoEffect(read: ReadSnapshot | undefined, effects: Act
|
|
|
159
161
|
export interface CollectOptions {
|
|
160
162
|
/** Dialogs the action opened */
|
|
161
163
|
dialogs: number;
|
|
164
|
+
/** Console messages logged since the action started, so far */
|
|
165
|
+
consoleMessages: () => number;
|
|
162
166
|
/** Decide "no effect" (with a second look when nothing seemed to happen) */
|
|
163
167
|
detectNoEffect: boolean;
|
|
164
168
|
/** List the elements the action showed */
|
|
@@ -208,7 +208,7 @@ export function pageNavigation(startHref, read, events) {
|
|
|
208
208
|
* Whether an action had no visible effect: the page was read before and
|
|
209
209
|
* after in the same document, it counted no DOM change, nothing made the
|
|
210
210
|
* check uncertain, and no navigation, message, shown element, request,
|
|
211
|
-
* dialog
|
|
211
|
+
* dialog, new window or console message happened.
|
|
212
212
|
*
|
|
213
213
|
* @param read - Page read after the action
|
|
214
214
|
* @param effects - Navigation and messages found
|
|
@@ -225,7 +225,8 @@ export function hadNoEffect(read, effects, activity) {
|
|
|
225
225
|
(effects.shown ?? []).length === 0 &&
|
|
226
226
|
activity.requests === 0 &&
|
|
227
227
|
activity.dialogs === 0 &&
|
|
228
|
-
!activity.opened
|
|
228
|
+
!activity.opened &&
|
|
229
|
+
activity.consoleMessages === 0);
|
|
229
230
|
}
|
|
230
231
|
/**
|
|
231
232
|
* Start watching an action's effects: listen for main-frame navigations,
|
|
@@ -265,7 +266,11 @@ async function collectEffects(watch, options) {
|
|
|
265
266
|
const reportShown = options.reportShown === true;
|
|
266
267
|
let snapshot = await readPage(watch, { stop: false, reportShown });
|
|
267
268
|
let effects = effectsOf(start, snapshot, watch.listener.events);
|
|
268
|
-
const quiet = () => hadNoEffect(snapshot, effects, {
|
|
269
|
+
const quiet = () => hadNoEffect(snapshot, effects, {
|
|
270
|
+
...watch.listener.activity(),
|
|
271
|
+
dialogs: options.dialogs,
|
|
272
|
+
consoleMessages: options.consoleMessages(),
|
|
273
|
+
});
|
|
269
274
|
if (options.detectNoEffect && quiet()) {
|
|
270
275
|
await delay(NO_EFFECT_RECHECK_MS);
|
|
271
276
|
snapshot = await readPage(watch, { stop: true, reportShown });
|
|
@@ -325,7 +330,8 @@ function pageWork(watch, snapshot, domChanging = false) {
|
|
|
325
330
|
}
|
|
326
331
|
/**
|
|
327
332
|
* Navigation, new messages and shown elements from the snapshots and CDP
|
|
328
|
-
* events.
|
|
333
|
+
* events. Snapshots a page broke (it replaced the built-ins the snapshot
|
|
334
|
+
* script uses) give the navigation only.
|
|
329
335
|
*
|
|
330
336
|
* @param start - Snapshot before the action, if taken
|
|
331
337
|
* @param snapshot - Read after the action, if taken
|
|
@@ -334,16 +340,22 @@ function pageWork(watch, snapshot, domChanging = false) {
|
|
|
334
340
|
*/
|
|
335
341
|
function effectsOf(start, snapshot, events) {
|
|
336
342
|
const navigation = pageNavigation(start?.href, snapshot, events);
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
343
|
+
try {
|
|
344
|
+
const { messages, more } = start && snapshot
|
|
345
|
+
? newMessages(start.messages, snapshot.messages, snapshot.fresh)
|
|
346
|
+
: { messages: [], more: 0 };
|
|
347
|
+
const shown = shownElements(snapshot?.shown ?? [], messages);
|
|
348
|
+
return {
|
|
349
|
+
...(navigation && { navigation }),
|
|
350
|
+
...(messages.length > 0 && { messages }),
|
|
351
|
+
...(more > 0 && { moreMessages: more }),
|
|
352
|
+
...(shown.length > 0 && { shown }),
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
catch (error) {
|
|
356
|
+
log.debug(`Page effects not readable (the page replaced built-ins?): ${getErrorMessage(error)}`);
|
|
357
|
+
return { ...(navigation && { navigation }) };
|
|
358
|
+
}
|
|
347
359
|
}
|
|
348
360
|
/**
|
|
349
361
|
* Read the page after the action, unless a main-frame load is pending (the
|
|
@@ -6,6 +6,7 @@ import { CommandError } from '../../errors/index.js';
|
|
|
6
6
|
import { operationFailedError } from '../../errors/messages.js';
|
|
7
7
|
import { buildAudit } from './auditModel.js';
|
|
8
8
|
import { AUDIT_PAGE_JS } from './auditScripts.js';
|
|
9
|
+
import { evaluateInBdgWorld } from '../page/bdgWorld.js';
|
|
9
10
|
import { EXIT_CODES } from '../../utils/exitCodes.js';
|
|
10
11
|
/** Findings listed per check without `--limit` */
|
|
11
12
|
export const DEFAULT_AUDIT_LIMIT = 20;
|
|
@@ -18,10 +19,10 @@ export const DEFAULT_AUDIT_LIMIT = 20;
|
|
|
18
19
|
* @throws CommandError (91) when the page script fails
|
|
19
20
|
*/
|
|
20
21
|
export async function auditPage(cdp, params) {
|
|
21
|
-
const response =
|
|
22
|
+
const response = await evaluateInBdgWorld(cdp, {
|
|
22
23
|
expression: `(${AUDIT_PAGE_JS})(${JSON.stringify(params.checks)})`,
|
|
23
24
|
returnByValue: true,
|
|
24
|
-
})
|
|
25
|
+
});
|
|
25
26
|
const raw = response.result.value;
|
|
26
27
|
if (response.exceptionDetails || !raw) {
|
|
27
28
|
const err = operationFailedError('audit the page', response.exceptionDetails?.exception?.description ?? 'the page script failed');
|
|
@@ -48,6 +48,7 @@ export function buildAudit(raw, options) {
|
|
|
48
48
|
...(animation.scrollDriven && { scrollDriven: true }),
|
|
49
49
|
}))).slice(0, options.limit),
|
|
50
50
|
}),
|
|
51
|
+
...(raw.canvases && { canvases: raw.canvases }),
|
|
51
52
|
};
|
|
52
53
|
}
|
|
53
54
|
/**
|
|
@@ -59,15 +60,18 @@ export function buildAudit(raw, options) {
|
|
|
59
60
|
*/
|
|
60
61
|
function contrastFindings(raw, options) {
|
|
61
62
|
const checked = (raw.texts ?? []).map((text) => contrastItem(text, raw.canvasDark));
|
|
62
|
-
const
|
|
63
|
+
const below = checked
|
|
63
64
|
.filter((item) => item !== undefined)
|
|
64
65
|
.filter((item) => item.ratio < requiredRatio(item, options.level))
|
|
65
66
|
.sort((a, b) => a.ratio - b.ratio);
|
|
67
|
+
const failing = below.filter((item) => !item.approximate);
|
|
68
|
+
const uncertain = below.length - failing.length;
|
|
66
69
|
return {
|
|
67
70
|
level: options.level,
|
|
68
71
|
checked: checked.length,
|
|
69
72
|
failing: failing.length,
|
|
70
73
|
items: failing.slice(0, options.limit),
|
|
74
|
+
...(uncertain > 0 && { uncertain }),
|
|
71
75
|
};
|
|
72
76
|
}
|
|
73
77
|
/**
|
|
@@ -95,6 +99,7 @@ function contrastItem(text, canvasDark) {
|
|
|
95
99
|
size: pxNumber(text.fontSize) ?? 16,
|
|
96
100
|
weight: Number(text.fontWeight) || 400,
|
|
97
101
|
inView: text.inView,
|
|
102
|
+
...(contrast.opacity !== undefined && { opacity: contrast.opacity }),
|
|
98
103
|
...(contrast.approximate && { approximate: contrast.approximate }),
|
|
99
104
|
};
|
|
100
105
|
}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* that collects what each check needs. Colors and contrast are computed in
|
|
7
7
|
* the daemon ({@link buildAudit}), the same way `dom inspect` computes them.
|
|
8
8
|
*/
|
|
9
|
-
import type
|
|
9
|
+
import { type RawBackground } from './inspectScripts.js';
|
|
10
10
|
/** Elements the walk looks at, at most */
|
|
11
11
|
export declare const AUDIT_ELEMENT_CAP = 20000;
|
|
12
12
|
/** A text holder: an element that draws text of its own */
|
|
@@ -19,7 +19,11 @@ export interface RawAuditText {
|
|
|
19
19
|
/** Backgrounds from the element up to the root, each with its own opacity */
|
|
20
20
|
backgrounds: RawBackground[];
|
|
21
21
|
opacity: number;
|
|
22
|
-
/**
|
|
22
|
+
/**
|
|
23
|
+
* Why its contrast is approximate: blend modes and filters on it or an
|
|
24
|
+
* ancestor, an element painted behind or on top of it (in view), or that
|
|
25
|
+
* only its ancestors were checked (out of view, none of them paints)
|
|
26
|
+
*/
|
|
23
27
|
risks: string[];
|
|
24
28
|
/** Inside the viewport */
|
|
25
29
|
inView: boolean;
|
|
@@ -98,10 +102,12 @@ export interface RawAudit {
|
|
|
98
102
|
}>;
|
|
99
103
|
layers?: RawLayer[];
|
|
100
104
|
animations?: RawAnimation[];
|
|
105
|
+
/** Visible `<canvas>` elements (their animations are drawn by scripts, not CSS or Web Animations) */
|
|
106
|
+
canvases?: number;
|
|
101
107
|
}
|
|
102
108
|
/**
|
|
103
109
|
* Page-side audit walk. Arguments: the checks to collect for
|
|
104
110
|
* (`contrast`, `overflow`, `layers`, `animations`).
|
|
105
111
|
*/
|
|
106
|
-
export declare const AUDIT_PAGE_JS
|
|
112
|
+
export declare const AUDIT_PAGE_JS: string;
|
|
107
113
|
//# sourceMappingURL=auditScripts.d.ts.map
|
|
@@ -6,6 +6,9 @@
|
|
|
6
6
|
* that collects what each check needs. Colors and contrast are computed in
|
|
7
7
|
* the daemon ({@link buildAudit}), the same way `dom inspect` computes them.
|
|
8
8
|
*/
|
|
9
|
+
import { LABEL_CLASSES_JS } from './elementInfo.js';
|
|
10
|
+
import { FLAT_TREE_JS, HIT_EVERYTHING_JS, PAINT_RISKS_JS, } from './inspectScripts.js';
|
|
11
|
+
import { AUDIT_OUT_OF_VIEW_RISK } from '../../ui/messages/commands.js';
|
|
9
12
|
/** Elements the walk looks at, at most */
|
|
10
13
|
export const AUDIT_ELEMENT_CAP = 20000;
|
|
11
14
|
/**
|
|
@@ -19,7 +22,8 @@ export const AUDIT_PAGE_JS = `function (checks) {
|
|
|
19
22
|
const style = (n) => view.getComputedStyle(n);
|
|
20
23
|
const scroller = doc.scrollingElement || doc.documentElement;
|
|
21
24
|
const viewport = { width: doc.documentElement.clientWidth, height: view.innerHeight };
|
|
22
|
-
const
|
|
25
|
+
const firstClass = (n) => (${LABEL_CLASSES_JS})(n)[0];
|
|
26
|
+
const label = (n) => n.localName + (n.id ? '#' + n.id : firstClass(n) ? '.' + firstClass(n) : '');
|
|
23
27
|
const parentOf = (n) => n.assignedSlot || n.parentElement || (n.parentNode && n.parentNode.host) || null;
|
|
24
28
|
const short = (text) => text.replace(/\\s+/g, ' ').trim().slice(0, 60);
|
|
25
29
|
const ownText = (n) => Array.from(n.childNodes).filter((c) => c.nodeType === 3).map((c) => c.data).join(' ').replace(/\\s+/g, ' ').trim();
|
|
@@ -30,20 +34,42 @@ export const AUDIT_PAGE_JS = `function (checks) {
|
|
|
30
34
|
const canvasDark = /dark/.test(scheme) && (!/light/.test(scheme) || view.matchMedia('(prefers-color-scheme: dark)').matches);
|
|
31
35
|
const result = { viewport: viewport, pixelRatio: view.devicePixelRatio || 1, pageWidth: scroller.scrollWidth, canvasDark: canvasDark, walked: 0, capped: false };
|
|
32
36
|
const texts = [], wide = [], truncated = [], images = [], layers = [], scrollers = [];
|
|
37
|
+
let canvases = 0;
|
|
38
|
+
const tree = (${FLAT_TREE_JS})(view);
|
|
39
|
+
const paintRisks = ${PAINT_RISKS_JS};
|
|
40
|
+
const clear = (color) => color === 'transparent' || /^rgba\\(.*,\\s*0\\)$/.test(color) || /\\/\\s*0\\)$/.test(color);
|
|
33
41
|
const chainOf = (n) => {
|
|
34
42
|
const backgrounds = [];
|
|
35
43
|
const risks = [];
|
|
36
44
|
let opacity = 1;
|
|
45
|
+
let painted = false;
|
|
46
|
+
let solid = false;
|
|
37
47
|
for (let p = n; p && backgrounds.length < 60; p = parentOf(p)) {
|
|
38
48
|
const s = style(p);
|
|
39
49
|
const own = Number(s.opacity) || 0;
|
|
40
50
|
const background = { color: s.backgroundColor, image: s.backgroundImage !== 'none' };
|
|
41
51
|
backgrounds.push(own < 1 ? Object.assign(background, { opacity: own }) : background);
|
|
42
52
|
opacity *= own;
|
|
53
|
+
if (p !== doc.documentElement && p !== doc.body && (background.image || !clear(s.backgroundColor))) painted = true;
|
|
54
|
+
if (p !== doc.documentElement && p !== doc.body && !background.image && /^rgb\\(/.test(s.backgroundColor)) solid = true;
|
|
43
55
|
if (s.mixBlendMode !== 'normal') risks.push('mix-blend-mode ' + s.mixBlendMode + ' on ' + label(p));
|
|
44
56
|
if (s.filter !== 'none') risks.push('filter on ' + label(p));
|
|
45
57
|
}
|
|
46
|
-
return { backgrounds: backgrounds, risks: risks, opacity: opacity };
|
|
58
|
+
return { backgrounds: backgrounds, risks: risks, opacity: opacity, painted: painted, solid: solid };
|
|
59
|
+
};
|
|
60
|
+
const unhit = [];
|
|
61
|
+
const painters = [];
|
|
62
|
+
const markPaintedBehind = () => {
|
|
63
|
+
for (const text of unhit) {
|
|
64
|
+
const painter = painters.find((p) => p.el !== text.el && !p.el.contains(text.el) && !text.el.contains(p.el) &&
|
|
65
|
+
text.x >= p.left && text.x <= p.right && text.y >= p.top && text.y <= p.bottom);
|
|
66
|
+
if (painter && !text.entry.risks.includes(${JSON.stringify(AUDIT_OUT_OF_VIEW_RISK)})) text.entry.risks.push(label(painter.el) + ' under or over it (out of view, not hit-tested)');
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
const risksOf = (n, slot, chain, shown) => {
|
|
70
|
+
if (!shown) return chain.painted ? chain.risks : chain.risks.concat(${JSON.stringify(AUDIT_OUT_OF_VIEW_RISK)});
|
|
71
|
+
const text = slot && n.assignedNodes({ flatten: true }).find((c) => c.nodeType === 3 && c.data.trim() !== '');
|
|
72
|
+
return paintRisks(n, tree, text ? text.parentElement || n : n);
|
|
47
73
|
};
|
|
48
74
|
const visit = (n, fixed) => {
|
|
49
75
|
if (result.walked >= ${AUDIT_ELEMENT_CAP}) { result.capped = true; return; }
|
|
@@ -57,14 +83,21 @@ export const AUDIT_PAGE_JS = `function (checks) {
|
|
|
57
83
|
const box = slot && parentOf(n) ? parentOf(n) : n;
|
|
58
84
|
const r = box.getBoundingClientRect();
|
|
59
85
|
const shown = r.width > 0 && r.height > 0 && s.visibility === 'visible';
|
|
60
|
-
const gradientText =
|
|
86
|
+
const gradientText = /rgba\\(0, 0, 0, 0\\)|transparent/.test(s.webkitTextFillColor);
|
|
61
87
|
if (shown && want('contrast') && !gradientText) {
|
|
62
88
|
const text = slot ? short(n.assignedNodes({ flatten: true }).filter((c) => c.nodeType === 3).map((c) => c.data).join(' ')) : n.shadowRoot ? '' : ownText(n);
|
|
63
89
|
if (text && Number(s.opacity) > 0 && r.width > 2 && r.height > 2) {
|
|
64
90
|
const chain = chainOf(n);
|
|
65
|
-
|
|
91
|
+
const seen = inView(r);
|
|
92
|
+
if (chain.opacity > 0) {
|
|
93
|
+
texts.push({ label: label(n), text: short(text), color: s.color, fontSize: s.fontSize, fontWeight: s.fontWeight, inView: seen, backgrounds: chain.backgrounds, opacity: chain.opacity, risks: risksOf(n, slot, chain, seen) });
|
|
94
|
+
if (!seen && !chain.solid) unhit.push({ entry: texts[texts.length - 1], el: box, x: (r.left + r.right) / 2 + view.scrollX, y: (r.top + r.bottom) / 2 + view.scrollY });
|
|
95
|
+
}
|
|
66
96
|
}
|
|
67
97
|
}
|
|
98
|
+
if (shown && want('contrast') && (/^(img|video|canvas|picture|iframe)$/.test(n.localName) || s.backgroundImage !== 'none')) {
|
|
99
|
+
painters.push({ el: n, left: r.left + view.scrollX, top: r.top + view.scrollY, right: r.right + view.scrollX, bottom: r.bottom + view.scrollY });
|
|
100
|
+
}
|
|
68
101
|
if (shown && want('overflow')) {
|
|
69
102
|
if (r.right > viewport.width + 1 && !inFixed) wide.push({ label: label(n), right: r.right + view.scrollX, width: r.width });
|
|
70
103
|
const cut = s.overflowX !== 'visible' && n.scrollWidth > n.clientWidth + 1 ? (s.textOverflow === 'ellipsis' ? 'ellipsis' : 'clip')
|
|
@@ -78,6 +111,7 @@ export const AUDIT_PAGE_JS = `function (checks) {
|
|
|
78
111
|
if (shown && want('overflow') && /^(auto|scroll)$/.test(s.overflowX) && n !== doc.documentElement && n !== doc.body && n.scrollWidth > n.clientWidth + 1) {
|
|
79
112
|
scrollers.push({ label: label(n), scrollWidth: n.scrollWidth, width: n.clientWidth });
|
|
80
113
|
}
|
|
114
|
+
if (want('animations') && shown && n.localName === 'canvas') canvases++;
|
|
81
115
|
if (want('layers') && shown && (s.position === 'fixed' || s.position === 'sticky')) {
|
|
82
116
|
layers.push({ label: label(n), position: s.position, zIndex: s.zIndex, rect: { x: r.left, y: r.top, w: r.width, h: r.height }, inView: inView(r) });
|
|
83
117
|
}
|
|
@@ -89,11 +123,13 @@ export const AUDIT_PAGE_JS = `function (checks) {
|
|
|
89
123
|
}
|
|
90
124
|
for (const c of kids) visit(c, inFixed);
|
|
91
125
|
};
|
|
92
|
-
visit(doc.documentElement, false);
|
|
126
|
+
(${HIT_EVERYTHING_JS})([doc], () => visit(doc.documentElement, false));
|
|
127
|
+
markPaintedBehind();
|
|
93
128
|
if (want('contrast')) result.texts = texts;
|
|
94
129
|
if (want('overflow')) Object.assign(result, { wide: wide, truncated: truncated, images: images, scrollers: scrollers });
|
|
95
130
|
if (want('layers')) result.layers = layers;
|
|
96
131
|
if (want('animations')) {
|
|
132
|
+
result.canvases = canvases;
|
|
97
133
|
result.animations = doc.getAnimations().filter((a) => a.playState === 'running').slice(0, 200).map((a) => {
|
|
98
134
|
const timing = a.effect && a.effect.getTiming ? a.effect.getTiming() : {};
|
|
99
135
|
const target = a.effect && a.effect.target;
|