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
|
@@ -8,22 +8,24 @@
|
|
|
8
8
|
* tree), `dom layout`'s measurement (page position, hidden, covered,
|
|
9
9
|
* offscreen) and, once the nodes are pushed to CDP, `CSS.getComputedStyleForNode`
|
|
10
10
|
* for the element, its layout parent and its `::before`/`::after`,
|
|
11
|
-
* `CSS.getPlatformFontsForNode` for its text and `DOM.getBoxModel`.
|
|
12
|
-
*
|
|
13
|
-
*
|
|
11
|
+
* `CSS.getPlatformFontsForNode` for its text and `DOM.getBoxModel`. After
|
|
12
|
+
* those, for the hints, `--rules` and `--why`, the matched rules
|
|
13
|
+
* (`CSS.getMatchedStylesForNode`, see {@link matchedStyles}). DOM and CSS are
|
|
14
|
+
* enabled on the first inspect and kept on.
|
|
14
15
|
*/
|
|
15
16
|
import { CommandError } from '../../errors/index.js';
|
|
16
17
|
import { operationFailedError, unknownCssPropertyError, whyAllPropertyError, } from '../../errors/messages.js';
|
|
17
18
|
import { throwIfInvalidSelector } from './formFillHelpers/shared.js';
|
|
18
19
|
import { selectedProps } from './inspectAllStyles.js';
|
|
19
20
|
import { buildCascadeFields } from './inspectCascadeModel.js';
|
|
20
|
-
import { buildInspectResult } from './inspectModel.js';
|
|
21
|
-
import { matchedStyles, sourceLabel, trackStyleSheets } from './inspectRules.js';
|
|
22
21
|
import { explainUnsetVariables } from './inspectHints.js';
|
|
22
|
+
import { buildInspectResult } from './inspectModel.js';
|
|
23
|
+
import { HINTS_BUDGET_MS, matchedStyles, RULES_BUDGET_MS, sourceLabel, trackStyleSheets, } from './inspectRules.js';
|
|
23
24
|
import { INSPECT_PAGE_JS, RELATED_NODE_JS, VARIABLE_SETTERS_JS, } from './inspectScripts.js';
|
|
24
25
|
import { DEFAULT_TREE_DEPTH, DEFAULT_TREE_LIMIT } from './inspectTree.js';
|
|
25
26
|
import { inspectLayout } from './layout.js';
|
|
26
27
|
import { DEEP_QUERY_JS, missingElementError, selectorArgsJS } from './targetNode.js';
|
|
28
|
+
import { evaluateInBdgWorld, resolveNodeInBdgWorld } from '../page/bdgWorld.js';
|
|
27
29
|
import { createLogger } from '../../ui/logging/index.js';
|
|
28
30
|
import { getErrorMessage } from '../../utils/errors.js';
|
|
29
31
|
import { EXIT_CODES } from '../../utils/exitCodes.js';
|
|
@@ -31,10 +33,6 @@ import { findSimilar } from '../../utils/suggestions.js';
|
|
|
31
33
|
const log = createLogger('dom');
|
|
32
34
|
/** Connections DOM and CSS were enabled on */
|
|
33
35
|
const stylesEnabled = new WeakSet();
|
|
34
|
-
/** Time allowed for the matched rules behind the default hints (large stylesheets take longer) */
|
|
35
|
-
const HINTS_BUDGET_MS = 1000;
|
|
36
|
-
/** Time allowed for them with --rules or --why */
|
|
37
|
-
const RULES_BUDGET_MS = 5000;
|
|
38
36
|
/** Distinguishes the object groups of concurrent calls */
|
|
39
37
|
let groupCounter = 0;
|
|
40
38
|
/**
|
|
@@ -170,7 +168,7 @@ function expandCustomPropertyPatterns(names, style) {
|
|
|
170
168
|
*
|
|
171
169
|
* @param cdp - CDP connection (stylesheet headers for the source labels)
|
|
172
170
|
* @param sources - What was read
|
|
173
|
-
* @returns Cascade fields, or `cascade: 'timeout' | 'failed'` when the rules were not read
|
|
171
|
+
* @returns Cascade fields, or `cascade: 'timeout' | 'failed' | 'skipped'` when the rules were not read
|
|
174
172
|
*/
|
|
175
173
|
function cascadeFields(cdp, sources) {
|
|
176
174
|
if (!sources.matched)
|
|
@@ -248,9 +246,7 @@ function pickedHow(params, count, index) {
|
|
|
248
246
|
* @returns Remote object id, or undefined when the node left the page
|
|
249
247
|
*/
|
|
250
248
|
async function resolveCachedNode(cdp, backendNodeId, objectGroup) {
|
|
251
|
-
const resolved = (await cdp
|
|
252
|
-
.send('DOM.resolveNode', { backendNodeId, objectGroup })
|
|
253
|
-
.catch((error) => {
|
|
249
|
+
const resolved = (await resolveNodeInBdgWorld(cdp, { backendNodeId, objectGroup }).catch((error) => {
|
|
254
250
|
log.debug(`Node ${backendNodeId} not resolved: ${getErrorMessage(error)}`);
|
|
255
251
|
return {};
|
|
256
252
|
}));
|
|
@@ -270,10 +266,10 @@ async function resolveCachedNode(cdp, backendNodeId, objectGroup) {
|
|
|
270
266
|
* @throws CommandError (81) invalid selector, (91) page script failure
|
|
271
267
|
*/
|
|
272
268
|
async function querySelector(cdp, selector, objectGroup) {
|
|
273
|
-
const response =
|
|
269
|
+
const response = await evaluateInBdgWorld(cdp, {
|
|
274
270
|
expression: `(${DEEP_QUERY_JS})(${selectorArgsJS(selector)})`,
|
|
275
271
|
objectGroup,
|
|
276
|
-
})
|
|
272
|
+
});
|
|
277
273
|
if (response.exceptionDetails || !response.result.objectId) {
|
|
278
274
|
if (response.exceptionDetails)
|
|
279
275
|
throwIfInvalidSelector(response.exceptionDetails, selector);
|
|
@@ -306,7 +302,10 @@ async function callOn(cdp, objectId, functionDeclaration, args) {
|
|
|
306
302
|
}
|
|
307
303
|
/**
|
|
308
304
|
* Read everything about the element: the page-side walk, `dom layout`'s
|
|
309
|
-
* measurement and the CDP styles, fonts and box.
|
|
305
|
+
* measurement and the CDP styles, fonts and box, then the matched rules.
|
|
306
|
+
* Chrome answers one request at a time and the rules can take seconds on
|
|
307
|
+
* CSS-heavy pages, so they are asked for last: the other reads do not wait
|
|
308
|
+
* behind them, only their own budget does.
|
|
310
309
|
*
|
|
311
310
|
* @param cdp - CDP connection
|
|
312
311
|
* @param objectId - The element
|
|
@@ -316,14 +315,16 @@ async function callOn(cdp, objectId, functionDeclaration, args) {
|
|
|
316
315
|
*/
|
|
317
316
|
async function readSources(cdp, objectId, params, objectGroup) {
|
|
318
317
|
const related = await relatedNodes(cdp, objectId, objectGroup);
|
|
319
|
-
const [raw, measured, styles] = await Promise.all([
|
|
318
|
+
const [raw, measured, { nodeId, ...styles }] = await Promise.all([
|
|
320
319
|
readPage(cdp, objectId, params),
|
|
321
320
|
measure(cdp, params.selector, related.node),
|
|
322
|
-
readStyles(cdp, related
|
|
321
|
+
readStyles(cdp, related),
|
|
323
322
|
]);
|
|
323
|
+
const matched = await readMatched(cdp, nodeId, params);
|
|
324
324
|
return {
|
|
325
325
|
raw,
|
|
326
326
|
...styles,
|
|
327
|
+
...(matched && { matched }),
|
|
327
328
|
fonts: raw.textHolder ? styles.fonts.textHolder : styles.fonts.node,
|
|
328
329
|
...measured,
|
|
329
330
|
...(params.rules && { rules: true }),
|
|
@@ -459,20 +460,19 @@ async function nodeIdLookup(cdp, related) {
|
|
|
459
460
|
}
|
|
460
461
|
/**
|
|
461
462
|
* Computed styles of the element, its parent and pseudo-elements, the
|
|
462
|
-
* platform fonts of its text
|
|
463
|
+
* platform fonts of its text, its border box size and its node id.
|
|
463
464
|
*
|
|
464
465
|
* @param cdp - CDP connection
|
|
465
466
|
* @param related - Backend node ids
|
|
466
467
|
* @returns CDP styles
|
|
467
468
|
*/
|
|
468
|
-
async function readStyles(cdp, related
|
|
469
|
+
async function readStyles(cdp, related) {
|
|
469
470
|
await enableStyleDomains(cdp);
|
|
470
471
|
const nodeIdOf = await nodeIdLookup(cdp, related);
|
|
471
472
|
const optionalStyle = (backendNodeId) => backendNodeId === undefined
|
|
472
473
|
? Promise.resolve(undefined)
|
|
473
474
|
: computedStyle(cdp, nodeIdOf(backendNodeId));
|
|
474
|
-
const [
|
|
475
|
-
readMatched(cdp, nodeIdOf(related.node), params),
|
|
475
|
+
const [style, parentStyle, holderStyle, nodeFonts, holderFonts, size, pseudo] = await Promise.all([
|
|
476
476
|
computedStyle(cdp, nodeIdOf(related.node)),
|
|
477
477
|
optionalStyle(related.parent),
|
|
478
478
|
optionalStyle(related.textHolder),
|
|
@@ -488,26 +488,39 @@ async function readStyles(cdp, related, params) {
|
|
|
488
488
|
pseudo,
|
|
489
489
|
fonts: { node: nodeFonts, textHolder: holderFonts },
|
|
490
490
|
...(size && { size }),
|
|
491
|
-
|
|
491
|
+
nodeId: nodeIdOf(related.node),
|
|
492
492
|
};
|
|
493
493
|
}
|
|
494
494
|
/**
|
|
495
|
-
*
|
|
496
|
-
* within {@link HINTS_BUDGET_MS} for the default hints
|
|
497
|
-
*
|
|
495
|
+
* How a request reads the element's matched rules, when hints, `--rules` or
|
|
496
|
+
* `--why` need them: within {@link HINTS_BUDGET_MS} for the default hints,
|
|
497
|
+
* not waited for at all on a document marked slow; within
|
|
498
|
+
* {@link RULES_BUDGET_MS} when asked for explicitly.
|
|
499
|
+
*
|
|
500
|
+
* @param params - Request
|
|
501
|
+
* @returns Budget and whether to skip on a slow document, or undefined when not needed
|
|
502
|
+
*/
|
|
503
|
+
export function matchedStylesRead(params) {
|
|
504
|
+
if (params.rules === true || params.why !== undefined) {
|
|
505
|
+
return { budgetMs: RULES_BUDGET_MS, skipWhenSlow: false };
|
|
506
|
+
}
|
|
507
|
+
if (params.hints === false || params.props !== undefined || params.all === true)
|
|
508
|
+
return undefined;
|
|
509
|
+
return { budgetMs: HINTS_BUDGET_MS, skipWhenSlow: true };
|
|
510
|
+
}
|
|
511
|
+
/**
|
|
512
|
+
* The element's matched rules, read as {@link matchedStylesRead} says.
|
|
498
513
|
*
|
|
499
514
|
* @param cdp - CDP connection
|
|
500
515
|
* @param nodeId - Node id of the element
|
|
501
516
|
* @param params - Request
|
|
502
|
-
* @returns Matched styles,
|
|
517
|
+
* @returns Matched styles or why they are missing, or undefined when not needed (or no node id)
|
|
503
518
|
*/
|
|
504
519
|
async function readMatched(cdp, nodeId, params) {
|
|
505
|
-
const
|
|
506
|
-
|
|
507
|
-
if (nodeId === undefined || skipped) {
|
|
520
|
+
const read = matchedStylesRead(params);
|
|
521
|
+
if (nodeId === undefined || !read)
|
|
508
522
|
return undefined;
|
|
509
|
-
}
|
|
510
|
-
return matchedStyles(cdp, nodeId, explicit ? RULES_BUDGET_MS : HINTS_BUDGET_MS);
|
|
523
|
+
return matchedStyles(cdp, nodeId, read.budgetMs, { skipWhenSlow: read.skipWhenSlow });
|
|
511
524
|
}
|
|
512
525
|
/**
|
|
513
526
|
* A pseudo-element's computed styles and size.
|
|
@@ -170,6 +170,7 @@ const PREFIXED_KEPT = new Set([
|
|
|
170
170
|
'-webkit-line-clamp',
|
|
171
171
|
'-webkit-text-stroke-width',
|
|
172
172
|
'-webkit-text-security',
|
|
173
|
+
'-webkit-text-fill-color',
|
|
173
174
|
]);
|
|
174
175
|
/** SVG paint and geometry properties (noise on HTML elements) */
|
|
175
176
|
const SVG_ONLY = /^(fill|fill-opacity|fill-rule|stroke|stroke-.*|stop-.*|flood-.*|lighting-color|marker-.*|clip-rule|color-interpolation.*|color-rendering|shape-rendering|text-anchor|dominant-baseline|alignment-baseline|baseline-shift|vector-effect|buffered-rendering|paint-order|mask-type)$/;
|
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
* checked against authored declarations only (never the browser's own
|
|
5
5
|
* styles), and `var()` references to custom properties that are not set.
|
|
6
6
|
*/
|
|
7
|
+
import type { InspectHint } from '../../ipc/protocol/inspectTypes.js';
|
|
7
8
|
import type { Declaration, Resolution } from './inspectCascade.js';
|
|
8
9
|
import type { StyleMap } from './inspectLayoutModel.js';
|
|
9
|
-
import type { InspectHint } from '../../ipc/protocol/inspectTypes.js';
|
|
10
10
|
import type { VariableSetter } from './inspectScripts.js';
|
|
11
11
|
/** A declaration that has no effect */
|
|
12
12
|
export interface CssHint {
|
|
@@ -5,11 +5,11 @@
|
|
|
5
5
|
* tree. Pure: every input is plain data, so the rules are tested without a
|
|
6
6
|
* browser.
|
|
7
7
|
*/
|
|
8
|
-
import type { Protocol } from '../../connection/typed-cdp.js';
|
|
9
8
|
import type { ElementLayout } from '../../ipc/protocol/domTypes.js';
|
|
10
9
|
import type { InspectResult, InspectVisibility } from '../../ipc/protocol/inspectTypes.js';
|
|
11
10
|
import { type StyleMap } from './inspectLayoutModel.js';
|
|
12
11
|
import { type PlatformFont, type PseudoSource } from './inspectPaintModel.js';
|
|
12
|
+
import type { MatchedStyles } from './inspectRules.js';
|
|
13
13
|
import type { RawInspect } from './inspectScripts.js';
|
|
14
14
|
/** What one inspect read */
|
|
15
15
|
export interface InspectSources {
|
|
@@ -29,8 +29,8 @@ export interface InspectSources {
|
|
|
29
29
|
/** The element as `dom layout` measures it */
|
|
30
30
|
layout?: ElementLayout;
|
|
31
31
|
colorScheme?: 'light' | 'dark';
|
|
32
|
-
/** Matched rules for the cascade fields; `timeout` or `
|
|
33
|
-
matched?:
|
|
32
|
+
/** Matched rules for the cascade fields; `timeout`, `failed` or `skipped` when they were not read */
|
|
33
|
+
matched?: MatchedStyles;
|
|
34
34
|
/** `--rules` was asked for */
|
|
35
35
|
rules?: boolean;
|
|
36
36
|
/** `--why` property */
|
|
@@ -52,7 +52,8 @@ export interface InspectRequest {
|
|
|
52
52
|
propValues?: InspectResult['props'];
|
|
53
53
|
}
|
|
54
54
|
/**
|
|
55
|
-
* The element's label: tag, id and the first classes
|
|
55
|
+
* The element's label: tag, id and the first classes ({@link isLabelClass}),
|
|
56
|
+
* with a count of the rest.
|
|
56
57
|
*
|
|
57
58
|
* @param raw - Tag, id and classes
|
|
58
59
|
* @returns e.g. `a.z-1.max-sm:hidden(+11)`, `input#user-name`
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* tree. Pure: every input is plain data, so the rules are tested without a
|
|
6
6
|
* browser.
|
|
7
7
|
*/
|
|
8
|
+
import { isLabelClass } from './elementInfo.js';
|
|
8
9
|
import { allStyles } from './inspectAllStyles.js';
|
|
9
10
|
import { buildBox, buildLayout } from './inspectLayoutModel.js';
|
|
10
11
|
import { buildEffects, buildFills, buildSvgPaint, buildFx, buildOutline, buildPseudo, buildRadius, buildState, buildStrokes, buildText, effectiveBackground, } from './inspectPaintModel.js';
|
|
@@ -14,17 +15,19 @@ import { normalizeCssValue, round1 } from '../../utils/cssValues.js';
|
|
|
14
15
|
/** Classes shown in the label (the rest are counted) */
|
|
15
16
|
const LABEL_CLASSES = 2;
|
|
16
17
|
/**
|
|
17
|
-
* The element's label: tag, id and the first classes
|
|
18
|
+
* The element's label: tag, id and the first classes ({@link isLabelClass}),
|
|
19
|
+
* with a count of the rest.
|
|
18
20
|
*
|
|
19
21
|
* @param raw - Tag, id and classes
|
|
20
22
|
* @returns e.g. `a.z-1.max-sm:hidden(+11)`, `input#user-name`
|
|
21
23
|
*/
|
|
22
24
|
export function elementLabel(raw) {
|
|
23
|
-
const
|
|
25
|
+
const classes = raw.classes.filter(isLabelClass);
|
|
26
|
+
const shown = classes
|
|
24
27
|
.slice(0, LABEL_CLASSES)
|
|
25
28
|
.map((name) => `.${name}`)
|
|
26
29
|
.join('');
|
|
27
|
-
const more =
|
|
30
|
+
const more = classes.length - LABEL_CLASSES;
|
|
28
31
|
return `${raw.tag}${raw.id ? `#${raw.id}` : ''}${shown}${more > 0 ? `(+${more})` : ''}`;
|
|
29
32
|
}
|
|
30
33
|
/**
|
|
@@ -48,6 +51,7 @@ export function visibilityOf(layout, rendered) {
|
|
|
48
51
|
...(offscreen && { offscreen }),
|
|
49
52
|
...(layout.coveredBy && { coveredBy: layout.coveredBy }),
|
|
50
53
|
...(layout.coverTransparent && { coverTransparent: true }),
|
|
54
|
+
...(layout.masked && { masked: layout.masked }),
|
|
51
55
|
};
|
|
52
56
|
}
|
|
53
57
|
/**
|
|
@@ -46,6 +46,8 @@ export declare function effectiveBackground(backgrounds: readonly RawBackground[
|
|
|
46
46
|
inherited: boolean;
|
|
47
47
|
overImage: boolean;
|
|
48
48
|
};
|
|
49
|
+
/** Why a contrast over a background image or gradient is approximate (its pixels are unknown) */
|
|
50
|
+
export declare const OVER_IMAGE_RISK = "background image or gradient behind";
|
|
49
51
|
/**
|
|
50
52
|
* Contrast of the text color with the background behind it, both painted
|
|
51
53
|
* as the browser composites them ({@link paintOver}), with what makes the
|
|
@@ -128,6 +128,8 @@ export function effectiveBackground(backgrounds, canvasDark) {
|
|
|
128
128
|
overImage: painted.overImage,
|
|
129
129
|
};
|
|
130
130
|
}
|
|
131
|
+
/** Why a contrast over a background image or gradient is approximate (its pixels are unknown) */
|
|
132
|
+
export const OVER_IMAGE_RISK = 'background image or gradient behind';
|
|
131
133
|
/**
|
|
132
134
|
* Contrast of the text color with the background behind it, both painted
|
|
133
135
|
* as the browser composites them ({@link paintOver}), with what makes the
|
|
@@ -146,7 +148,7 @@ export function textContrast(style, raw) {
|
|
|
146
148
|
const ratio = Math.floor(contrastRatio(painted.text, painted.background) * 100) / 100;
|
|
147
149
|
const size = pxNumber(style['font-size']) ?? 16;
|
|
148
150
|
const weight = Number(style['font-weight'] ?? 400);
|
|
149
|
-
const approximate = raw.paintRisks ?? [];
|
|
151
|
+
const approximate = [...(raw.paintRisks ?? []), ...(painted.overImage ? [OVER_IMAGE_RISK] : [])];
|
|
150
152
|
return {
|
|
151
153
|
ratio,
|
|
152
154
|
level: contrastLevel(ratio, size, weight),
|
|
@@ -22,16 +22,42 @@ export declare function trackStyleSheets(cdp: CDPConnection): void;
|
|
|
22
22
|
* @returns Headers
|
|
23
23
|
*/
|
|
24
24
|
export declare function styleSheetHeaders(cdp: CDPConnection): Iterable<Protocol.CSS.CSSStyleSheetHeader>;
|
|
25
|
+
/** Time allowed for the matched rules behind the default hints; a read within it clears the slow mark */
|
|
26
|
+
export declare const HINTS_BUDGET_MS = 1000;
|
|
27
|
+
/** Time allowed for them with --rules or --why */
|
|
28
|
+
export declare const RULES_BUDGET_MS = 5000;
|
|
29
|
+
/** Matched styles of an element, or why they are missing */
|
|
30
|
+
export type MatchedStyles = Protocol.CSS.GetMatchedStylesForNodeResponse | 'timeout' | 'failed' | 'skipped';
|
|
31
|
+
/**
|
|
32
|
+
* Forget the kept answers of a connection (requests still running stay
|
|
33
|
+
* shared). For commands that may change the page in ways CDP reports no
|
|
34
|
+
* event for: clicks, typing, hovering, scripts, emulation.
|
|
35
|
+
*
|
|
36
|
+
* @param cdp - CDP connection
|
|
37
|
+
*/
|
|
38
|
+
export declare function resetMatchedStyles(cdp: CDPConnection): void;
|
|
25
39
|
/**
|
|
26
40
|
* The rules that match an element, with its inline style and what its
|
|
27
|
-
* ancestors pass down, or why they are missing.
|
|
41
|
+
* ancestors pass down, or why they are missing. A request still running for
|
|
42
|
+
* the element is shared; a slow answer (over {@link KEEP_ANSWERS_SLOWER_THAN_MS})
|
|
43
|
+
* is reused for {@link KEPT_ANSWER_TTL_MS} unless the document, its
|
|
44
|
+
* stylesheets or its DOM change or a command may have changed the page.
|
|
45
|
+
* Another element's request is sent only after the one Chrome is working on,
|
|
46
|
+
* within the budget. When the request this call sent or shared outlasts the
|
|
47
|
+
* budget, the document is marked slow: `skipWhenSlow` reads then return
|
|
48
|
+
* `skipped` at once (unless the answer is kept) until a read is fast again,
|
|
49
|
+
* a stylesheet changes or the page navigates.
|
|
28
50
|
*
|
|
29
51
|
* @param cdp - CDP connection
|
|
30
52
|
* @param nodeId - Node id of the element
|
|
31
53
|
* @param budgetMs - Time allowed
|
|
32
|
-
* @
|
|
54
|
+
* @param options - `skipWhenSlow`: do not wait on a document marked slow (the default hints)
|
|
55
|
+
* @returns Matched styles, `timeout` (longer than the budget), `failed` (CDP error)
|
|
56
|
+
* or `skipped` (slow document)
|
|
33
57
|
*/
|
|
34
|
-
export declare function matchedStyles(cdp: CDPConnection, nodeId: number, budgetMs: number
|
|
58
|
+
export declare function matchedStyles(cdp: CDPConnection, nodeId: number, budgetMs: number, options?: {
|
|
59
|
+
skipWhenSlow?: boolean;
|
|
60
|
+
}): Promise<MatchedStyles>;
|
|
35
61
|
/**
|
|
36
62
|
* Where a declaration comes from, for people: the selector and the file
|
|
37
63
|
* with its line (and column, for single-line minified files), or what kind
|
|
@@ -37,24 +37,218 @@ export function trackStyleSheets(cdp) {
|
|
|
37
37
|
export function styleSheetHeaders(cdp) {
|
|
38
38
|
return headersByConnection.get(cdp)?.values() ?? [];
|
|
39
39
|
}
|
|
40
|
+
/** Time allowed for the matched rules behind the default hints; a read within it clears the slow mark */
|
|
41
|
+
export const HINTS_BUDGET_MS = 1000;
|
|
42
|
+
/** Time allowed for them with --rules or --why */
|
|
43
|
+
export const RULES_BUDGET_MS = 5000;
|
|
44
|
+
/** Answers that came faster are not kept: reading again is cheap and always current */
|
|
45
|
+
const KEEP_ANSWERS_SLOWER_THAN_MS = 300;
|
|
46
|
+
/**
|
|
47
|
+
* How long a kept answer is reused. Page state such as `:checked`, `:hover`
|
|
48
|
+
* or `:focus` changes without any CDP event, so answers are kept only briefly
|
|
49
|
+
* (and dropped by every command that may change the page, see {@link resetMatchedStyles}).
|
|
50
|
+
*/
|
|
51
|
+
const KEPT_ANSWER_TTL_MS = 5000;
|
|
52
|
+
/** Answers kept per document (one can be several MB on CSS-heavy pages) */
|
|
53
|
+
const MAX_KEPT_ANSWERS = 4;
|
|
54
|
+
/** Stylesheet events: kept answers are dropped and the slow mark cleared */
|
|
55
|
+
const STYLESHEET_EVENTS = ['CSS.styleSheetAdded', 'CSS.styleSheetChanged', 'CSS.styleSheetRemoved'];
|
|
56
|
+
/** Other events after which kept answers may be stale */
|
|
57
|
+
const STYLE_CHANGE_EVENTS = [
|
|
58
|
+
'CSS.mediaQueryResultChanged',
|
|
59
|
+
'DOM.attributeModified',
|
|
60
|
+
'DOM.attributeRemoved',
|
|
61
|
+
'DOM.inlineStyleInvalidated',
|
|
62
|
+
'DOM.childNodeInserted',
|
|
63
|
+
'DOM.childNodeRemoved',
|
|
64
|
+
'DOM.childNodeCountUpdated',
|
|
65
|
+
'DOM.pseudoElementAdded',
|
|
66
|
+
'DOM.pseudoElementRemoved',
|
|
67
|
+
];
|
|
68
|
+
/** Matched-styles state per connection */
|
|
69
|
+
const documentStylesByConnection = new WeakMap();
|
|
70
|
+
/**
|
|
71
|
+
* The matched-styles state of a connection, tracked from its first use: a
|
|
72
|
+
* new document (`DOM.documentUpdated`) starts afresh (a request still running
|
|
73
|
+
* for the old one no longer holds back new ones), stylesheet changes drop the
|
|
74
|
+
* kept answers and the slow mark, style and DOM changes the kept answers.
|
|
75
|
+
* Events of other sessions (iframes) are ignored.
|
|
76
|
+
*
|
|
77
|
+
* @param cdp - CDP connection
|
|
78
|
+
* @returns State
|
|
79
|
+
*/
|
|
80
|
+
function documentStyles(cdp) {
|
|
81
|
+
const existing = documentStylesByConnection.get(cdp);
|
|
82
|
+
if (existing)
|
|
83
|
+
return existing;
|
|
84
|
+
const state = { requests: new Map(), slow: false, document: 0 };
|
|
85
|
+
documentStylesByConnection.set(cdp, state);
|
|
86
|
+
cdp.on('DOM.documentUpdated', (_params, sessionId) => {
|
|
87
|
+
if (sessionId)
|
|
88
|
+
return;
|
|
89
|
+
state.document++;
|
|
90
|
+
state.requests.clear();
|
|
91
|
+
state.running = undefined;
|
|
92
|
+
state.slow = false;
|
|
93
|
+
});
|
|
94
|
+
for (const event of STYLESHEET_EVENTS) {
|
|
95
|
+
cdp.on(event, (_params, sessionId) => {
|
|
96
|
+
if (sessionId)
|
|
97
|
+
return;
|
|
98
|
+
state.requests.clear();
|
|
99
|
+
state.slow = false;
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
for (const event of STYLE_CHANGE_EVENTS) {
|
|
103
|
+
cdp.on(event, (_params, sessionId) => {
|
|
104
|
+
if (!sessionId)
|
|
105
|
+
state.requests.clear();
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
return state;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Forget the kept answers of a connection (requests still running stay
|
|
112
|
+
* shared). For commands that may change the page in ways CDP reports no
|
|
113
|
+
* event for: clicks, typing, hovering, scripts, emulation.
|
|
114
|
+
*
|
|
115
|
+
* @param cdp - CDP connection
|
|
116
|
+
*/
|
|
117
|
+
export function resetMatchedStyles(cdp) {
|
|
118
|
+
documentStylesByConnection.get(cdp)?.requests.clear();
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Record an answer: a fast one clears the slow mark and is dropped, a slow
|
|
122
|
+
* one is kept for {@link KEPT_ANSWER_TTL_MS}, a failed one is dropped.
|
|
123
|
+
*
|
|
124
|
+
* @param state - Document state
|
|
125
|
+
* @param nodeId - Node id of the element
|
|
126
|
+
* @param request - The request
|
|
127
|
+
* @param tookMs - How long Chrome took
|
|
128
|
+
*/
|
|
129
|
+
function settleRequest(state, nodeId, request, tookMs) {
|
|
130
|
+
if (state.running === request.promise)
|
|
131
|
+
state.running = undefined;
|
|
132
|
+
if (request.document !== state.document)
|
|
133
|
+
return;
|
|
134
|
+
const answered = request.answer !== 'failed';
|
|
135
|
+
if (answered && tookMs <= HINTS_BUDGET_MS)
|
|
136
|
+
state.slow = false;
|
|
137
|
+
if (answered && tookMs > KEEP_ANSWERS_SLOWER_THAN_MS) {
|
|
138
|
+
request.expiresAt = Date.now() + KEPT_ANSWER_TTL_MS;
|
|
139
|
+
}
|
|
140
|
+
else if (state.requests.get(nodeId) === request) {
|
|
141
|
+
state.requests.delete(nodeId);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Send a matched-styles request and share it while it runs, dropping the
|
|
146
|
+
* least recently used entries beyond {@link MAX_KEPT_ANSWERS}.
|
|
147
|
+
*
|
|
148
|
+
* @param cdp - CDP connection
|
|
149
|
+
* @param state - Document state
|
|
150
|
+
* @param nodeId - Node id of the element
|
|
151
|
+
* @returns The request
|
|
152
|
+
*/
|
|
153
|
+
function sendMatchedRequest(cdp, state, nodeId) {
|
|
154
|
+
const sentAt = Date.now();
|
|
155
|
+
const request = {
|
|
156
|
+
document: state.document,
|
|
157
|
+
promise: cdp
|
|
158
|
+
.send('CSS.getMatchedStylesForNode', { nodeId })
|
|
159
|
+
.then((response) => response)
|
|
160
|
+
.catch((error) => {
|
|
161
|
+
log.debug(`CSS.getMatchedStylesForNode failed: ${String(error)}`);
|
|
162
|
+
return 'failed';
|
|
163
|
+
}),
|
|
164
|
+
};
|
|
165
|
+
state.running = request.promise;
|
|
166
|
+
void request.promise.then((answer) => {
|
|
167
|
+
request.answer = answer;
|
|
168
|
+
settleRequest(state, nodeId, request, Date.now() - sentAt);
|
|
169
|
+
});
|
|
170
|
+
state.requests.set(nodeId, request);
|
|
171
|
+
for (const oldest of state.requests.keys()) {
|
|
172
|
+
if (state.requests.size <= MAX_KEPT_ANSWERS)
|
|
173
|
+
break;
|
|
174
|
+
state.requests.delete(oldest);
|
|
175
|
+
}
|
|
176
|
+
return request;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* The element's request still running or kept answer, unless expired;
|
|
180
|
+
* marked as the most recently used.
|
|
181
|
+
*
|
|
182
|
+
* @param state - Document state
|
|
183
|
+
* @param nodeId - Node id of the element
|
|
184
|
+
* @returns The request, or undefined
|
|
185
|
+
*/
|
|
186
|
+
function keptRequest(state, nodeId) {
|
|
187
|
+
const kept = state.requests.get(nodeId);
|
|
188
|
+
if (!kept)
|
|
189
|
+
return undefined;
|
|
190
|
+
state.requests.delete(nodeId);
|
|
191
|
+
if (kept.expiresAt !== undefined && kept.expiresAt <= Date.now())
|
|
192
|
+
return undefined;
|
|
193
|
+
state.requests.set(nodeId, kept);
|
|
194
|
+
return kept;
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* The element's request, sent once Chrome has answered the one it is
|
|
198
|
+
* working on (another element's, or this one's sent by a concurrent call).
|
|
199
|
+
*
|
|
200
|
+
* @param cdp - CDP connection
|
|
201
|
+
* @param state - Document state
|
|
202
|
+
* @param nodeId - Node id of the element
|
|
203
|
+
* @param deadline - When to give up waiting
|
|
204
|
+
* @returns The request, or undefined when the running one outlasted the deadline
|
|
205
|
+
*/
|
|
206
|
+
async function requestWhenFree(cdp, state, nodeId, deadline) {
|
|
207
|
+
while (state.running) {
|
|
208
|
+
const kept = state.requests.get(nodeId);
|
|
209
|
+
if (kept)
|
|
210
|
+
return kept;
|
|
211
|
+
if ((await raceTimeout(state.running, deadline - Date.now())) === undefined)
|
|
212
|
+
return undefined;
|
|
213
|
+
}
|
|
214
|
+
return state.requests.get(nodeId) ?? sendMatchedRequest(cdp, state, nodeId);
|
|
215
|
+
}
|
|
40
216
|
/**
|
|
41
217
|
* The rules that match an element, with its inline style and what its
|
|
42
|
-
* ancestors pass down, or why they are missing.
|
|
218
|
+
* ancestors pass down, or why they are missing. A request still running for
|
|
219
|
+
* the element is shared; a slow answer (over {@link KEEP_ANSWERS_SLOWER_THAN_MS})
|
|
220
|
+
* is reused for {@link KEPT_ANSWER_TTL_MS} unless the document, its
|
|
221
|
+
* stylesheets or its DOM change or a command may have changed the page.
|
|
222
|
+
* Another element's request is sent only after the one Chrome is working on,
|
|
223
|
+
* within the budget. When the request this call sent or shared outlasts the
|
|
224
|
+
* budget, the document is marked slow: `skipWhenSlow` reads then return
|
|
225
|
+
* `skipped` at once (unless the answer is kept) until a read is fast again,
|
|
226
|
+
* a stylesheet changes or the page navigates.
|
|
43
227
|
*
|
|
44
228
|
* @param cdp - CDP connection
|
|
45
229
|
* @param nodeId - Node id of the element
|
|
46
230
|
* @param budgetMs - Time allowed
|
|
47
|
-
* @
|
|
231
|
+
* @param options - `skipWhenSlow`: do not wait on a document marked slow (the default hints)
|
|
232
|
+
* @returns Matched styles, `timeout` (longer than the budget), `failed` (CDP error)
|
|
233
|
+
* or `skipped` (slow document)
|
|
48
234
|
*/
|
|
49
|
-
export async function matchedStyles(cdp, nodeId, budgetMs) {
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
.
|
|
54
|
-
|
|
55
|
-
return '
|
|
56
|
-
|
|
57
|
-
|
|
235
|
+
export async function matchedStyles(cdp, nodeId, budgetMs, options = {}) {
|
|
236
|
+
const state = documentStyles(cdp);
|
|
237
|
+
const kept = keptRequest(state, nodeId);
|
|
238
|
+
if (kept?.answer)
|
|
239
|
+
return kept.answer;
|
|
240
|
+
if (options.skipWhenSlow && state.slow)
|
|
241
|
+
return 'skipped';
|
|
242
|
+
const deadline = Date.now() + budgetMs;
|
|
243
|
+
const request = kept ?? (await requestWhenFree(cdp, state, nodeId, deadline));
|
|
244
|
+
if (!request)
|
|
245
|
+
return 'timeout';
|
|
246
|
+
const answer = await raceTimeout(request.promise, deadline - Date.now());
|
|
247
|
+
if (answer)
|
|
248
|
+
return answer;
|
|
249
|
+
if (request.document === state.document)
|
|
250
|
+
state.slow = true;
|
|
251
|
+
return 'timeout';
|
|
58
252
|
}
|
|
59
253
|
/**
|
|
60
254
|
* File name of a URL, without query or hash; the host for a site's root.
|