browser-debugger-cli 0.11.0 → 0.12.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 +142 -79
- package/dist/commands/css.d.ts +13 -0
- package/dist/commands/css.js +53 -0
- package/dist/commands/dom/audit.d.ts +14 -0
- package/dist/commands/dom/audit.js +87 -0
- package/dist/commands/dom/formInteraction.js +36 -6
- package/dist/commands/dom/helpers/keyAttributes.d.ts +3 -2
- package/dist/commands/dom/helpers/keyAttributes.js +6 -4
- package/dist/commands/dom/helpers/screenshot.d.ts +1 -0
- package/dist/commands/dom/helpers/screenshot.js +158 -38
- package/dist/commands/dom/index.js +4 -1
- package/dist/commands/dom/screenshot.js +10 -6
- package/dist/commands/dom/wait.js +5 -3
- package/dist/commands/helpJson.js +1 -1
- package/dist/commands/optionBehaviors.js +21 -6
- package/dist/commands/page.js +7 -4
- package/dist/commands/peek.d.ts +7 -0
- package/dist/commands/peek.js +65 -23
- package/dist/commands/shared/optionTypes.d.ts +5 -1
- package/dist/commands/start.d.ts +13 -0
- package/dist/commands/start.js +19 -2
- package/dist/commands/tail.d.ts +7 -1
- package/dist/commands/tail.js +13 -62
- package/dist/commands.js +2 -0
- package/dist/daemon/session/commandRegistry.js +7 -1
- package/dist/daemon/session/plugins.js +4 -52
- package/dist/daemon.js +7986 -6848
- package/dist/errors/messages.d.ts +34 -4
- package/dist/errors/messages.js +68 -5
- package/dist/index.js +610 -186
- package/dist/ipc/client.d.ts +4 -0
- package/dist/ipc/client.js +8 -0
- package/dist/ipc/protocol/auditTypes.d.ts +129 -0
- package/dist/ipc/protocol/auditTypes.js +6 -0
- package/dist/ipc/protocol/commands.d.ts +23 -0
- package/dist/ipc/protocol/commands.js +2 -0
- package/dist/ipc/protocol/domTypes.d.ts +4 -0
- package/dist/ipc/protocol/inspectTypes.d.ts +71 -8
- package/dist/runtime/css/search.d.ts +39 -0
- package/dist/runtime/css/search.js +122 -0
- package/dist/runtime/dom/actionEffects.d.ts +4 -1
- package/dist/runtime/dom/actionEffects.js +8 -4
- package/dist/runtime/dom/audit.d.ts +19 -0
- package/dist/runtime/dom/audit.js +36 -0
- package/dist/runtime/dom/auditModel.d.ts +45 -0
- package/dist/runtime/dom/auditModel.js +215 -0
- package/dist/runtime/dom/auditScripts.d.ts +107 -0
- package/dist/runtime/dom/auditScripts.js +112 -0
- package/dist/runtime/dom/elementGeometry.d.ts +8 -2
- package/dist/runtime/dom/elementGeometry.js +24 -8
- package/dist/runtime/dom/elementInfo.d.ts +3 -2
- package/dist/runtime/dom/elementInfo.js +8 -2
- package/dist/runtime/dom/formFillHelpers/fill.js +2 -2
- package/dist/runtime/dom/inspect.d.ts +7 -0
- package/dist/runtime/dom/inspect.js +88 -23
- package/dist/runtime/dom/inspectAllStyles.d.ts +16 -4
- package/dist/runtime/dom/inspectAllStyles.js +89 -7
- package/dist/runtime/dom/inspectCascade.d.ts +19 -2
- package/dist/runtime/dom/inspectCascade.js +214 -44
- package/dist/runtime/dom/inspectCascadeModel.d.ts +8 -0
- package/dist/runtime/dom/inspectCascadeModel.js +108 -34
- package/dist/runtime/dom/inspectHints.d.ts +26 -3
- package/dist/runtime/dom/inspectHints.js +125 -9
- package/dist/runtime/dom/inspectModel.d.ts +3 -0
- package/dist/runtime/dom/inspectModel.js +30 -7
- package/dist/runtime/dom/inspectPaintModel.d.ts +48 -22
- package/dist/runtime/dom/inspectPaintModel.js +180 -68
- package/dist/runtime/dom/inspectRules.d.ts +19 -0
- package/dist/runtime/dom/inspectRules.js +21 -5
- package/dist/runtime/dom/inspectScripts.d.ts +85 -12
- package/dist/runtime/dom/inspectScripts.js +314 -28
- package/dist/runtime/dom/inspectTree.js +10 -2
- package/dist/runtime/dom/inspectWhyModel.d.ts +2 -1
- package/dist/runtime/dom/inspectWhyModel.js +52 -10
- package/dist/runtime/dom/layout.js +31 -9
- package/dist/runtime/dom/reactEventHelpers.d.ts +7 -0
- package/dist/runtime/dom/reactEventHelpers.js +27 -9
- package/dist/runtime/page/emulation.d.ts +13 -4
- package/dist/runtime/page/emulation.js +69 -4
- package/dist/runtime/page/userAgent.d.ts +17 -0
- package/dist/runtime/page/userAgent.js +57 -0
- package/dist/types.d.ts +4 -0
- package/dist/ui/formatters/audit.d.ts +19 -0
- package/dist/ui/formatters/audit.js +106 -0
- package/dist/ui/formatters/dom.d.ts +1 -1
- package/dist/ui/formatters/dom.js +6 -3
- package/dist/ui/formatters/inspect.js +42 -15
- package/dist/ui/formatters/status.js +1 -1
- package/dist/ui/messages/commands.d.ts +44 -7
- package/dist/ui/messages/commands.js +83 -11
- package/dist/ui/messages/preview.d.ts +6 -0
- package/dist/ui/messages/preview.js +9 -1
- package/dist/utils/cssValues.js +36 -4
- package/dist/utils/decisionTrees.js +0 -5
- package/dist/utils/suggestions.d.ts +4 -2
- package/dist/utils/suggestions.js +7 -5
- package/dist/utils/taskMappings.js +1 -1
- package/package.json +3 -2
|
@@ -225,7 +225,8 @@ export const ELEMENT_IDENTITY_JS = `(el) => {
|
|
|
225
225
|
}`;
|
|
226
226
|
/**
|
|
227
227
|
* Page-side location of an element: the iframes it is in (outermost first)
|
|
228
|
-
* and the shadow root holding it,
|
|
228
|
+
* and the shadow root holding it, with the host's first class and light-DOM
|
|
229
|
+
* text, e.g. `iframe#pay > shadow root of <sl-button.primary "Save">`;
|
|
229
230
|
* empty for the main document.
|
|
230
231
|
*/
|
|
231
232
|
export const ELEMENT_CONTEXT_JS = `(el) => {
|
|
@@ -237,7 +238,12 @@ export const ELEMENT_CONTEXT_JS = `(el) => {
|
|
|
237
238
|
doc = frame.ownerDocument;
|
|
238
239
|
}
|
|
239
240
|
const root = el.getRootNode();
|
|
240
|
-
if (root.host)
|
|
241
|
+
if (root.host) {
|
|
242
|
+
const host = root.host;
|
|
243
|
+
const label = describe(host) + (!host.id && host.classList.length ? '.' + host.classList[0] : '');
|
|
244
|
+
const text = (host.textContent || '').replace(/\\s+/g, ' ').trim().slice(0, 30);
|
|
245
|
+
parts.push('shadow root of <' + label + (text ? ' "' + text + '"' : '') + '>');
|
|
246
|
+
}
|
|
241
247
|
return parts.join(' > ');
|
|
242
248
|
}`;
|
|
243
249
|
/**
|
|
@@ -7,7 +7,7 @@ import * as path from 'node:path';
|
|
|
7
7
|
import { CommandError } from '../../../errors/index.js';
|
|
8
8
|
import { fileNotFoundError, uploadDirectoryError, singleFileInputError, fillableElementNotFoundError, clickableElementNotFoundError, clickTargetDetachedError, unexpectedResponseFormatError, operationFailedError, pressNotReceivedError, unreachableElementError, } from '../../../errors/messages.js';
|
|
9
9
|
import { escapeValueForJS, formatScriptExecutionError, throwIfInvalidSelector, withMultipleMatchesWarning, withValueMismatchWarning, } from './shared.js';
|
|
10
|
-
import { FILL_READ_BACK_SCRIPT, REACT_FILL_SCRIPT, CLICK_ELEMENT_SCRIPT, isFillResult, isClickResult, } from '../reactEventHelpers.js';
|
|
10
|
+
import { FILL_READ_BACK_SCRIPT, REACT_FILL_SCRIPT, CLICK_ELEMENT_SCRIPT, SHADOW_FIELD_JS, isFillResult, isClickResult, } from '../reactEventHelpers.js';
|
|
11
11
|
import { FIND_ELEMENTS_JS, LABEL_CONTROL_JS, selectorArgsJS } from '../targetNode.js';
|
|
12
12
|
import { createLogger } from '../../../ui/logging/index.js';
|
|
13
13
|
import { CLICK_NOT_RECEIVED_WARNING, POINTER_ACTION_DONE, POINTER_ACTION_NOUN, domClickFallbackWarning, } from '../../../ui/messages/commands.js';
|
|
@@ -176,7 +176,7 @@ async function setFileInput(cdp, selector, value, options) {
|
|
|
176
176
|
return problem;
|
|
177
177
|
try {
|
|
178
178
|
const located = (await cdp.send('Runtime.evaluate', {
|
|
179
|
-
expression: `((el) => (${LABEL_CONTROL_JS})(el) || el)((${FIND_ELEMENTS_JS})(${selectorArgsJS(selector)})[${options.index ?? 0}])`,
|
|
179
|
+
expression: `((el) => (${LABEL_CONTROL_JS})(el) || (${SHADOW_FIELD_JS})(el) || el)((${FIND_ELEMENTS_JS})(${selectorArgsJS(selector)})[${options.index ?? 0}])`,
|
|
180
180
|
objectGroup: UPLOAD_OBJECT_GROUP,
|
|
181
181
|
}));
|
|
182
182
|
if (located.exceptionDetails)
|
|
@@ -25,4 +25,11 @@ import type { InspectResult } from '../../ipc/protocol/inspectTypes.js';
|
|
|
25
25
|
* selector or unknown property, (87) the cached element left the page
|
|
26
26
|
*/
|
|
27
27
|
export declare function inspectElement(cdp: CDPConnection, params: DomInspectCommand): Promise<InspectResult>;
|
|
28
|
+
/**
|
|
29
|
+
* Enable DOM and CSS once per connection (kept on: CSS.enable replays every
|
|
30
|
+
* stylesheet, which costs up to a few hundred ms on large sites the first time).
|
|
31
|
+
*
|
|
32
|
+
* @param cdp - CDP connection
|
|
33
|
+
*/
|
|
34
|
+
export declare function enableStyleDomains(cdp: CDPConnection): Promise<void>;
|
|
28
35
|
//# sourceMappingURL=inspect.d.ts.map
|
|
@@ -13,13 +13,14 @@
|
|
|
13
13
|
* read (no cascade).
|
|
14
14
|
*/
|
|
15
15
|
import { CommandError } from '../../errors/index.js';
|
|
16
|
-
import { operationFailedError, unknownCssPropertyError } from '../../errors/messages.js';
|
|
16
|
+
import { operationFailedError, unknownCssPropertyError, whyAllPropertyError, } from '../../errors/messages.js';
|
|
17
17
|
import { throwIfInvalidSelector } from './formFillHelpers/shared.js';
|
|
18
18
|
import { selectedProps } from './inspectAllStyles.js';
|
|
19
19
|
import { buildCascadeFields } from './inspectCascadeModel.js';
|
|
20
20
|
import { buildInspectResult } from './inspectModel.js';
|
|
21
21
|
import { matchedStyles, sourceLabel, trackStyleSheets } from './inspectRules.js';
|
|
22
|
-
import {
|
|
22
|
+
import { explainUnsetVariables } from './inspectHints.js';
|
|
23
|
+
import { INSPECT_PAGE_JS, RELATED_NODE_JS, VARIABLE_SETTERS_JS, } from './inspectScripts.js';
|
|
23
24
|
import { DEFAULT_TREE_DEPTH, DEFAULT_TREE_LIMIT } from './inspectTree.js';
|
|
24
25
|
import { inspectLayout } from './layout.js';
|
|
25
26
|
import { DEEP_QUERY_JS, missingElementError, selectorArgsJS } from './targetNode.js';
|
|
@@ -36,12 +37,18 @@ const HINTS_BUDGET_MS = 1000;
|
|
|
36
37
|
const RULES_BUDGET_MS = 5000;
|
|
37
38
|
/** Distinguishes the object groups of concurrent calls */
|
|
38
39
|
let groupCounter = 0;
|
|
39
|
-
/**
|
|
40
|
+
/**
|
|
41
|
+
* Page-side choice of a match: the index asked for, else the first visible
|
|
42
|
+
* one (rendered, not `visibility: hidden`, not `opacity: 0`), else the first
|
|
43
|
+
* rendered one, else the first
|
|
44
|
+
*/
|
|
40
45
|
const PICK_MATCH_JS = `function (i) {
|
|
41
46
|
if (i !== null) return i;
|
|
42
|
-
const
|
|
43
|
-
const
|
|
44
|
-
|
|
47
|
+
const check = (el, options) => (el.checkVisibility ? el.checkVisibility(options) : el.getClientRects().length > 0);
|
|
48
|
+
const visible = Array.prototype.findIndex.call(this, (el) => check(el, { visibilityProperty: true, opacityProperty: true }));
|
|
49
|
+
if (visible >= 0) return visible;
|
|
50
|
+
const rendered = Array.prototype.findIndex.call(this, (el) => check(el, {}));
|
|
51
|
+
return rendered < 0 ? 0 : rendered;
|
|
45
52
|
}`;
|
|
46
53
|
/**
|
|
47
54
|
* Inspect one element.
|
|
@@ -54,10 +61,20 @@ const PICK_MATCH_JS = `function (i) {
|
|
|
54
61
|
*/
|
|
55
62
|
export async function inspectElement(cdp, params) {
|
|
56
63
|
const started = Date.now();
|
|
64
|
+
if (params.why === 'all') {
|
|
65
|
+
const err = whyAllPropertyError();
|
|
66
|
+
throw new CommandError(err.message, { suggestion: err.suggestion }, EXIT_CODES.INVALID_ARGUMENTS);
|
|
67
|
+
}
|
|
68
|
+
const pseudoOf = /::?(before|after)\s*$/i.exec(params.selector)?.[1]?.toLowerCase();
|
|
69
|
+
const request = pseudoOf
|
|
70
|
+
? { ...params, selector: params.selector.replace(/::?(before|after)\s*$/i, '') }
|
|
71
|
+
: params;
|
|
57
72
|
const objectGroup = `bdg-inspect-${++groupCounter}`;
|
|
58
73
|
try {
|
|
59
|
-
const found = await findElement(cdp,
|
|
60
|
-
const sources = await readSources(cdp, found.objectId,
|
|
74
|
+
const found = await findElement(cdp, request, objectGroup);
|
|
75
|
+
const sources = await readSources(cdp, found.objectId, request, objectGroup);
|
|
76
|
+
if (sources.raw.unknownWhy)
|
|
77
|
+
throwUnknownProperties([params.why ?? ''], sources);
|
|
61
78
|
const propValues = sources.props ? checkedProps(sources.props, sources) : undefined;
|
|
62
79
|
const built = buildInspectResult(sources, {
|
|
63
80
|
selector: params.selector,
|
|
@@ -67,8 +84,14 @@ export async function inspectElement(cdp, params) {
|
|
|
67
84
|
...(params.all && { all: true }),
|
|
68
85
|
...(propValues && { propValues }),
|
|
69
86
|
});
|
|
70
|
-
const
|
|
71
|
-
const
|
|
87
|
+
const cascade = cascadeFields(cdp, sources);
|
|
88
|
+
const hints = cascade.hints && (await explainedHints(cdp, found.objectId, cascade.hints));
|
|
89
|
+
const withCascade = { ...built, ...cascade, ...(hints && { hints }) };
|
|
90
|
+
const result = {
|
|
91
|
+
...withCascade,
|
|
92
|
+
...(found.picked && { picked: found.picked }),
|
|
93
|
+
...(pseudoOf && { pseudoOf: `::${pseudoOf}` }),
|
|
94
|
+
};
|
|
72
95
|
log.debug(`Inspected ${result.element} in ${Date.now() - started} ms`);
|
|
73
96
|
return result;
|
|
74
97
|
}
|
|
@@ -78,6 +101,25 @@ export async function inspectElement(cdp, params) {
|
|
|
78
101
|
.catch((error) => log.debug(`Object group not released: ${getErrorMessage(error)}`));
|
|
79
102
|
}
|
|
80
103
|
}
|
|
104
|
+
/**
|
|
105
|
+
* Hints with unset variables told where the page sets them
|
|
106
|
+
* ({@link VARIABLE_SETTERS_JS}); unchanged when there are none or the
|
|
107
|
+
* search fails.
|
|
108
|
+
*
|
|
109
|
+
* @param cdp - CDP connection
|
|
110
|
+
* @param objectId - The element
|
|
111
|
+
* @param hints - Hints
|
|
112
|
+
* @returns Hints
|
|
113
|
+
*/
|
|
114
|
+
async function explainedHints(cdp, objectId, hints) {
|
|
115
|
+
const names = [...new Set(hints.flatMap((hint) => hint.variables ?? []))];
|
|
116
|
+
if (names.length === 0)
|
|
117
|
+
return hints;
|
|
118
|
+
const setters = await callOn(cdp, objectId, VARIABLE_SETTERS_JS, [
|
|
119
|
+
names,
|
|
120
|
+
]);
|
|
121
|
+
return setters ? explainUnsetVariables(hints, setters) : hints;
|
|
122
|
+
}
|
|
81
123
|
/**
|
|
82
124
|
* The properties asked for with `--props`.
|
|
83
125
|
*
|
|
@@ -87,9 +129,19 @@ export async function inspectElement(cdp, params) {
|
|
|
87
129
|
* @throws CommandError (81) for a name no value was found for
|
|
88
130
|
*/
|
|
89
131
|
function checkedProps(names, sources) {
|
|
90
|
-
const { props, unknown } = selectedProps(names, sources.style, sources.raw.props, sources.raw.unknownProps);
|
|
132
|
+
const { props, unknown } = selectedProps(names, sources.style, sources.raw.props, sources.raw.unknownProps, sources.raw.rootFontSize);
|
|
91
133
|
if (unknown.length === 0)
|
|
92
134
|
return props;
|
|
135
|
+
return throwUnknownProperties(unknown, sources);
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Reject names that are not CSS properties, with the closest computed ones.
|
|
139
|
+
*
|
|
140
|
+
* @param unknown - Names
|
|
141
|
+
* @param sources - What was read (computed property names)
|
|
142
|
+
* @throws CommandError (81) always
|
|
143
|
+
*/
|
|
144
|
+
function throwUnknownProperties(unknown, sources) {
|
|
93
145
|
const suggestions = unknown.flatMap((name) => findSimilar(name, Object.keys(sources.style), { maxSuggestions: 1 }));
|
|
94
146
|
const err = unknownCssPropertyError(unknown, suggestions);
|
|
95
147
|
throw new CommandError(err.message, { suggestion: err.suggestion }, EXIT_CODES.INVALID_ARGUMENTS);
|
|
@@ -130,11 +182,14 @@ function cascadeFields(cdp, sources) {
|
|
|
130
182
|
style: sources.style,
|
|
131
183
|
parentStyle: sources.parentStyle,
|
|
132
184
|
replaced: sources.raw.replaced === true,
|
|
133
|
-
formControl: sources.raw.formControl,
|
|
185
|
+
formControl: sources.raw.formControl && sources.raw.hasText,
|
|
134
186
|
...(sources.hints === false && { hints: false }),
|
|
135
187
|
label: (declaration) => sourceLabel(declaration, cdp),
|
|
136
188
|
...(sources.rules && { rules: true }),
|
|
137
189
|
...(sources.why && { why: sources.why }),
|
|
190
|
+
...(sources.raw.whyLonghands && { whyLonghands: sources.raw.whyLonghands }),
|
|
191
|
+
...(sources.raw.whyComputed && { whyComputed: sources.raw.whyComputed }),
|
|
192
|
+
...(sources.raw.whyInactive && { whyInactive: sources.raw.whyInactive }),
|
|
138
193
|
...(sources.props && { props: sources.props }),
|
|
139
194
|
});
|
|
140
195
|
}
|
|
@@ -269,7 +324,7 @@ async function readSources(cdp, objectId, params, objectGroup) {
|
|
|
269
324
|
return {
|
|
270
325
|
raw,
|
|
271
326
|
...styles,
|
|
272
|
-
fonts: raw.
|
|
327
|
+
fonts: raw.textHolder ? styles.fonts.textHolder : styles.fonts.node,
|
|
273
328
|
...measured,
|
|
274
329
|
...(params.rules && { rules: true }),
|
|
275
330
|
...(params.why && { why: params.why }),
|
|
@@ -290,6 +345,7 @@ async function readPage(cdp, objectId, params) {
|
|
|
290
345
|
const raw = await callOn(cdp, objectId, INSPECT_PAGE_JS, [
|
|
291
346
|
params.props || params.all ? 0 : (params.tree ?? DEFAULT_TREE_DEPTH),
|
|
292
347
|
params.props ?? null,
|
|
348
|
+
params.why ?? null,
|
|
293
349
|
]);
|
|
294
350
|
if (raw)
|
|
295
351
|
return raw;
|
|
@@ -297,11 +353,12 @@ async function readPage(cdp, objectId, params) {
|
|
|
297
353
|
throw new CommandError(err.message, { suggestion: err.suggestion }, EXIT_CODES.SCRIPT_ERROR);
|
|
298
354
|
}
|
|
299
355
|
/**
|
|
300
|
-
* The backend node id of the element's layout parent
|
|
356
|
+
* The backend node id of the element's layout parent, the element that
|
|
357
|
+
* draws most of its text, or the parent of that text's nodes.
|
|
301
358
|
*
|
|
302
359
|
* @param cdp - CDP connection
|
|
303
360
|
* @param objectId - The element
|
|
304
|
-
* @param which - `parent` or `
|
|
361
|
+
* @param which - `parent`, `textHolder` or `fontHolder`
|
|
305
362
|
* @param objectGroup - Object group for handles
|
|
306
363
|
* @returns Backend node id, or undefined when there is none
|
|
307
364
|
*/
|
|
@@ -316,8 +373,8 @@ async function relatedNode(cdp, objectId, which, objectGroup) {
|
|
|
316
373
|
return id ? (await describe(cdp, id)).backendNodeId : undefined;
|
|
317
374
|
}
|
|
318
375
|
/**
|
|
319
|
-
* The backend node ids of the element, its layout parent, its
|
|
320
|
-
*
|
|
376
|
+
* The backend node ids of the element, its layout parent, its text holder
|
|
377
|
+
* (with the parent of its text nodes) and its generated pseudo-elements.
|
|
321
378
|
*
|
|
322
379
|
* @param cdp - CDP connection
|
|
323
380
|
* @param objectId - The element
|
|
@@ -325,10 +382,11 @@ async function relatedNode(cdp, objectId, which, objectGroup) {
|
|
|
325
382
|
* @returns Backend node ids
|
|
326
383
|
*/
|
|
327
384
|
async function relatedNodes(cdp, objectId, objectGroup) {
|
|
328
|
-
const [node, parent, textHolder] = await Promise.all([
|
|
385
|
+
const [node, parent, textHolder, fontHolder] = await Promise.all([
|
|
329
386
|
describe(cdp, objectId),
|
|
330
387
|
relatedNode(cdp, objectId, 'parent', objectGroup),
|
|
331
388
|
relatedNode(cdp, objectId, 'textHolder', objectGroup),
|
|
389
|
+
relatedNode(cdp, objectId, 'fontHolder', objectGroup),
|
|
332
390
|
]);
|
|
333
391
|
const pseudo = (node.pseudoElements ?? [])
|
|
334
392
|
.filter((p) => p.pseudoType === 'before' || p.pseudoType === 'after')
|
|
@@ -339,7 +397,8 @@ async function relatedNodes(cdp, objectId, objectGroup) {
|
|
|
339
397
|
return {
|
|
340
398
|
node: node.backendNodeId,
|
|
341
399
|
...(parent && { parent }),
|
|
342
|
-
...(textHolder && { textHolder }),
|
|
400
|
+
...(textHolder && textHolder !== node.backendNodeId && { textHolder }),
|
|
401
|
+
...(fontHolder && { fontHolder }),
|
|
343
402
|
pseudo,
|
|
344
403
|
};
|
|
345
404
|
}
|
|
@@ -392,6 +451,7 @@ async function nodeIdLookup(cdp, related) {
|
|
|
392
451
|
related.node,
|
|
393
452
|
related.parent,
|
|
394
453
|
related.textHolder,
|
|
454
|
+
related.fontHolder,
|
|
395
455
|
...related.pseudo.map((p) => p.backendNodeId),
|
|
396
456
|
];
|
|
397
457
|
const ids = await pushNodes(cdp, order.filter((id) => id !== undefined));
|
|
@@ -408,18 +468,23 @@ async function nodeIdLookup(cdp, related) {
|
|
|
408
468
|
async function readStyles(cdp, related, params) {
|
|
409
469
|
await enableStyleDomains(cdp);
|
|
410
470
|
const nodeIdOf = await nodeIdLookup(cdp, related);
|
|
411
|
-
const
|
|
471
|
+
const optionalStyle = (backendNodeId) => backendNodeId === undefined
|
|
472
|
+
? Promise.resolve(undefined)
|
|
473
|
+
: computedStyle(cdp, nodeIdOf(backendNodeId));
|
|
474
|
+
const [matched, style, parentStyle, holderStyle, nodeFonts, holderFonts, size, pseudo] = await Promise.all([
|
|
412
475
|
readMatched(cdp, nodeIdOf(related.node), params),
|
|
413
476
|
computedStyle(cdp, nodeIdOf(related.node)),
|
|
414
|
-
|
|
477
|
+
optionalStyle(related.parent),
|
|
478
|
+
optionalStyle(related.textHolder),
|
|
415
479
|
platformFonts(cdp, nodeIdOf(related.node)),
|
|
416
|
-
platformFonts(cdp, nodeIdOf(related.
|
|
480
|
+
platformFonts(cdp, nodeIdOf(related.fontHolder)),
|
|
417
481
|
borderBoxSize(cdp, related.node),
|
|
418
482
|
Promise.all(related.pseudo.map((p) => pseudoSource(cdp, p.type, p.backendNodeId, nodeIdOf(p.backendNodeId)))),
|
|
419
483
|
]);
|
|
420
484
|
return {
|
|
421
485
|
style,
|
|
422
486
|
...(parentStyle && { parentStyle }),
|
|
487
|
+
...(holderStyle && { holderStyle }),
|
|
423
488
|
pseudo,
|
|
424
489
|
fonts: { node: nodeFonts, textHolder: holderFonts },
|
|
425
490
|
...(size && { size }),
|
|
@@ -466,7 +531,7 @@ async function pseudoSource(cdp, type, backendNodeId, nodeId) {
|
|
|
466
531
|
*
|
|
467
532
|
* @param cdp - CDP connection
|
|
468
533
|
*/
|
|
469
|
-
async function enableStyleDomains(cdp) {
|
|
534
|
+
export async function enableStyleDomains(cdp) {
|
|
470
535
|
if (stylesEnabled.has(cdp))
|
|
471
536
|
return;
|
|
472
537
|
trackStyleSheets(cdp);
|
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
*
|
|
4
4
|
* `--all` lists every computed longhand that is not noise and not its
|
|
5
5
|
* default, collapsed into shorthands (margin, padding, inset, border,
|
|
6
|
-
* radius, overflow, gap, flex, grid area, outline, animation). Defaults are
|
|
6
|
+
* radius, overflow, gap, flex, grid area, outline, animation, transition). Defaults are
|
|
7
7
|
* the CSS initial values as Chrome computes them; the UA stylesheet's
|
|
8
8
|
* per-tag values (a `<p>`'s margins, a button's padding) are real values and
|
|
9
9
|
* are listed. Display, font, size, line height and color are always listed.
|
|
10
10
|
*
|
|
11
11
|
* Noise: custom properties, logical duplicates of physical properties,
|
|
12
|
-
* width/height (in the header),
|
|
12
|
+
* width/height (in the header), origins without a transform,
|
|
13
13
|
* colors that only repeat `color` (currentColor), properties of borders,
|
|
14
14
|
* outlines and rules that are not drawn, SVG paint properties on HTML
|
|
15
15
|
* elements, and vendor-prefixed internals.
|
|
@@ -34,15 +34,25 @@ export declare function isDefaultValue(name: string, value: string): boolean;
|
|
|
34
34
|
* @returns Normalized value
|
|
35
35
|
*/
|
|
36
36
|
export declare function normalizeProperty(name: string, value: string): string;
|
|
37
|
+
/**
|
|
38
|
+
* A shorthand's computed value as `--all` writes it (`margin 0 8`,
|
|
39
|
+
* `outline 2 solid #f00`, `transition color 1s ease-in`).
|
|
40
|
+
*
|
|
41
|
+
* @param shorthand - Shorthand name
|
|
42
|
+
* @param style - Computed longhands
|
|
43
|
+
* @returns Value, or undefined for a shorthand `--all` does not collapse
|
|
44
|
+
*/
|
|
45
|
+
export declare function collapsedValue(shorthand: string, style: StyleMap): string | undefined;
|
|
37
46
|
/**
|
|
38
47
|
* Every computed longhand that is not noise and not a default, normalized
|
|
39
48
|
* and collapsed into shorthands.
|
|
40
49
|
*
|
|
41
50
|
* @param style - Computed styles
|
|
42
51
|
* @param svg - The element is an SVG element (SVG properties are listed)
|
|
52
|
+
* @param formControl - A form control (`appearance: none` is not its default)
|
|
43
53
|
* @returns Properties and values
|
|
44
54
|
*/
|
|
45
|
-
export declare function allStyles(style: StyleMap, svg: boolean): Record<string, string>;
|
|
55
|
+
export declare function allStyles(style: StyleMap, svg: boolean, formControl?: boolean): Record<string, string>;
|
|
46
56
|
/**
|
|
47
57
|
* The properties asked for with `--props`, raw and normalized. CDP's
|
|
48
58
|
* computed longhands come first; shorthands and custom properties come from
|
|
@@ -52,10 +62,12 @@ export declare function allStyles(style: StyleMap, svg: boolean): Record<string,
|
|
|
52
62
|
* @param style - Computed longhands (CDP)
|
|
53
63
|
* @param pageValues - Values the page read
|
|
54
64
|
* @param pageUnknown - Names the page does not know as CSS properties
|
|
65
|
+
* @param rootFontSize - Root font size (px): a custom property that is one
|
|
66
|
+
* rem length is also given in px
|
|
55
67
|
* @returns Values by name (an unset custom property is empty), and the
|
|
56
68
|
* names that are not properties
|
|
57
69
|
*/
|
|
58
|
-
export declare function selectedProps(names: readonly string[], style: StyleMap, pageValues: Record<string, string> | undefined, pageUnknown?: readonly string[]): {
|
|
70
|
+
export declare function selectedProps(names: readonly string[], style: StyleMap, pageValues: Record<string, string> | undefined, pageUnknown?: readonly string[], rootFontSize?: number): {
|
|
59
71
|
props: Record<string, InspectProp>;
|
|
60
72
|
unknown: string[];
|
|
61
73
|
};
|
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
*
|
|
4
4
|
* `--all` lists every computed longhand that is not noise and not its
|
|
5
5
|
* default, collapsed into shorthands (margin, padding, inset, border,
|
|
6
|
-
* radius, overflow, gap, flex, grid area, outline, animation). Defaults are
|
|
6
|
+
* radius, overflow, gap, flex, grid area, outline, animation, transition). Defaults are
|
|
7
7
|
* the CSS initial values as Chrome computes them; the UA stylesheet's
|
|
8
8
|
* per-tag values (a `<p>`'s margins, a button's padding) are real values and
|
|
9
9
|
* are listed. Display, font, size, line height and color are always listed.
|
|
10
10
|
*
|
|
11
11
|
* Noise: custom properties, logical duplicates of physical properties,
|
|
12
|
-
* width/height (in the header),
|
|
12
|
+
* width/height (in the header), origins without a transform,
|
|
13
13
|
* colors that only repeat `color` (currentColor), properties of borders,
|
|
14
14
|
* outlines and rules that are not drawn, SVG paint properties on HTML
|
|
15
15
|
* elements, and vendor-prefixed internals.
|
|
@@ -98,6 +98,28 @@ const INITIAL_BY_VALUE = [
|
|
|
98
98
|
['collapse', 'white-space-collapse'],
|
|
99
99
|
['horizontal-tb', 'writing-mode'],
|
|
100
100
|
['isolate', 'unicode-bidi'],
|
|
101
|
+
['rgb(0, 0, 0)', 'fill flood-color stop-color'],
|
|
102
|
+
['1', 'fill-opacity flood-opacity stop-opacity stroke-opacity'],
|
|
103
|
+
['rgb(255, 255, 255)', 'lighting-color'],
|
|
104
|
+
['1px', 'stroke-width'],
|
|
105
|
+
['4', 'stroke-miterlimit'],
|
|
106
|
+
['butt', 'stroke-linecap'],
|
|
107
|
+
['miter', 'stroke-linejoin'],
|
|
108
|
+
['nonzero', 'fill-rule clip-rule'],
|
|
109
|
+
['none', 'stroke stroke-dasharray marker-start marker-mid marker-end vector-effect'],
|
|
110
|
+
['0px', 'stroke-dashoffset baseline-shift'],
|
|
111
|
+
[
|
|
112
|
+
'auto',
|
|
113
|
+
'shape-rendering dominant-baseline alignment-baseline buffered-rendering color-rendering',
|
|
114
|
+
],
|
|
115
|
+
['srgb', 'color-interpolation'],
|
|
116
|
+
['linearrgb', 'color-interpolation-filters'],
|
|
117
|
+
['all', 'transition-property'],
|
|
118
|
+
['0s', 'transition-duration transition-delay'],
|
|
119
|
+
['ease', 'transition-timing-function'],
|
|
120
|
+
['normal', 'transition-behavior paint-order'],
|
|
121
|
+
['start', 'text-anchor'],
|
|
122
|
+
['rgba(0, 0, 0, 0.18)', '-webkit-tap-highlight-color'],
|
|
101
123
|
];
|
|
102
124
|
/** Initial value per property ({@link INITIAL_BY_VALUE}) */
|
|
103
125
|
const INITIAL_VALUES = new Map(INITIAL_BY_VALUE.flatMap(([value, names]) => names.split(' ').map((name) => [name, value])));
|
|
@@ -144,6 +166,7 @@ const NOISE = new Set([
|
|
|
144
166
|
const LOGICAL = /((^|-)block(-|$)|(^|-)inline(-|$)|^inset-|-start-start|-start-end|-end-end|-end-start)/;
|
|
145
167
|
/** Vendor-prefixed properties worth listing */
|
|
146
168
|
const PREFIXED_KEPT = new Set([
|
|
169
|
+
'-webkit-tap-highlight-color',
|
|
147
170
|
'-webkit-line-clamp',
|
|
148
171
|
'-webkit-text-stroke-width',
|
|
149
172
|
'-webkit-text-security',
|
|
@@ -161,7 +184,9 @@ const CURRENT_COLOR_ECHOES = /^(border-(top|right|bottom|left)-color|outline-col
|
|
|
161
184
|
* @returns True when it is left out
|
|
162
185
|
*/
|
|
163
186
|
function isNoise(name, style, svg) {
|
|
164
|
-
if (name.startsWith('--') || NOISE.has(name)
|
|
187
|
+
if (name.startsWith('--') || NOISE.has(name))
|
|
188
|
+
return true;
|
|
189
|
+
if (name.startsWith('transition') && style['transition-duration'] === '0s')
|
|
165
190
|
return true;
|
|
166
191
|
if (LOGICAL.test(name) && !name.endsWith('-axis'))
|
|
167
192
|
return true;
|
|
@@ -266,7 +291,56 @@ const COLLAPSES = [
|
|
|
266
291
|
],
|
|
267
292
|
write: (v) => v.join(' '),
|
|
268
293
|
},
|
|
294
|
+
{
|
|
295
|
+
shorthand: 'transition',
|
|
296
|
+
longhands: [
|
|
297
|
+
'transition-property',
|
|
298
|
+
'transition-duration',
|
|
299
|
+
'transition-timing-function',
|
|
300
|
+
'transition-delay',
|
|
301
|
+
'transition-behavior',
|
|
302
|
+
],
|
|
303
|
+
write: transitionList,
|
|
304
|
+
},
|
|
269
305
|
];
|
|
306
|
+
/**
|
|
307
|
+
* A transition list from its longhands' comma lists, one transition per
|
|
308
|
+
* property, defaults left out (`background-color 0.25s ease-in, opacity 0.2s`).
|
|
309
|
+
*
|
|
310
|
+
* @param values - Property, duration, timing function, delay and behavior lists
|
|
311
|
+
* @returns Transition shorthand
|
|
312
|
+
*/
|
|
313
|
+
function transitionList(values) {
|
|
314
|
+
const lists = values.map((value) => value.split(/,\s*(?![^(]*\))/));
|
|
315
|
+
const [properties = []] = lists;
|
|
316
|
+
return properties
|
|
317
|
+
.map((property, i) => {
|
|
318
|
+
const at = (list) => list?.[i % list.length] ?? '';
|
|
319
|
+
const [duration, timing, delay, behavior] = lists.slice(1).map(at);
|
|
320
|
+
return [
|
|
321
|
+
property,
|
|
322
|
+
duration,
|
|
323
|
+
timing === 'ease' ? '' : timing,
|
|
324
|
+
delay === '0s' ? '' : delay,
|
|
325
|
+
behavior === 'normal' ? '' : behavior,
|
|
326
|
+
]
|
|
327
|
+
.filter(Boolean)
|
|
328
|
+
.join(' ');
|
|
329
|
+
})
|
|
330
|
+
.join(', ');
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* A shorthand's computed value as `--all` writes it (`margin 0 8`,
|
|
334
|
+
* `outline 2 solid #f00`, `transition color 1s ease-in`).
|
|
335
|
+
*
|
|
336
|
+
* @param shorthand - Shorthand name
|
|
337
|
+
* @param style - Computed longhands
|
|
338
|
+
* @returns Value, or undefined for a shorthand `--all` does not collapse
|
|
339
|
+
*/
|
|
340
|
+
export function collapsedValue(shorthand, style) {
|
|
341
|
+
const group = COLLAPSES.find((entry) => entry.shorthand === shorthand);
|
|
342
|
+
return group?.write(group.longhands.map((longhand) => normalizeProperty(longhand, style[longhand] ?? '')));
|
|
343
|
+
}
|
|
270
344
|
/**
|
|
271
345
|
* Border longhands collapsed: one `border` when the four drawn sides are
|
|
272
346
|
* equal, else one entry per drawn side.
|
|
@@ -337,13 +411,15 @@ function isUnsetOffset(name, value, style) {
|
|
|
337
411
|
*
|
|
338
412
|
* @param style - Computed styles
|
|
339
413
|
* @param svg - The element is an SVG element (SVG properties are listed)
|
|
414
|
+
* @param formControl - A form control (`appearance: none` is not its default)
|
|
340
415
|
* @returns Properties and values
|
|
341
416
|
*/
|
|
342
|
-
export function allStyles(style, svg) {
|
|
417
|
+
export function allStyles(style, svg, formControl = false) {
|
|
343
418
|
const kept = new Map();
|
|
344
419
|
const transformed = (style['transform'] ?? 'none') !== 'none';
|
|
345
420
|
for (const [name, value] of Object.entries(style)) {
|
|
346
|
-
|
|
421
|
+
const set = (name === 'transform-origin' && transformed) || (name === 'appearance' && formControl);
|
|
422
|
+
if (set) {
|
|
347
423
|
kept.set(name, normalizeProperty(name, value));
|
|
348
424
|
continue;
|
|
349
425
|
}
|
|
@@ -364,10 +440,12 @@ export function allStyles(style, svg) {
|
|
|
364
440
|
* @param style - Computed longhands (CDP)
|
|
365
441
|
* @param pageValues - Values the page read
|
|
366
442
|
* @param pageUnknown - Names the page does not know as CSS properties
|
|
443
|
+
* @param rootFontSize - Root font size (px): a custom property that is one
|
|
444
|
+
* rem length is also given in px
|
|
367
445
|
* @returns Values by name (an unset custom property is empty), and the
|
|
368
446
|
* names that are not properties
|
|
369
447
|
*/
|
|
370
|
-
export function selectedProps(names, style, pageValues, pageUnknown = []) {
|
|
448
|
+
export function selectedProps(names, style, pageValues, pageUnknown = [], rootFontSize = 16) {
|
|
371
449
|
const props = {};
|
|
372
450
|
const unknown = [];
|
|
373
451
|
for (const name of names) {
|
|
@@ -377,7 +455,11 @@ export function selectedProps(names, style, pageValues, pageUnknown = []) {
|
|
|
377
455
|
unknown.push(name);
|
|
378
456
|
}
|
|
379
457
|
else {
|
|
380
|
-
|
|
458
|
+
const rem = name.startsWith('--') ? /^(-?\d*\.?\d+)rem$/.exec(computed.trim()) : null;
|
|
459
|
+
const value = rem
|
|
460
|
+
? String(Math.round(Number(rem[1]) * rootFontSize * 10) / 10)
|
|
461
|
+
: normalizeProperty(name, computed);
|
|
462
|
+
props[name] = { computed, value };
|
|
381
463
|
}
|
|
382
464
|
}
|
|
383
465
|
return { props, unknown };
|
|
@@ -32,6 +32,11 @@ export interface DeclarationSource {
|
|
|
32
32
|
condition?: string;
|
|
33
33
|
/** Specificity of the matching selector (ids, classes, types) */
|
|
34
34
|
specificity?: [number, number, number];
|
|
35
|
+
/** The rule as written, whitespace collapsed (no selector: the style attribute) */
|
|
36
|
+
rule?: {
|
|
37
|
+
selector?: string;
|
|
38
|
+
declarations: string;
|
|
39
|
+
};
|
|
35
40
|
}
|
|
36
41
|
/** One declaration of a longhand */
|
|
37
42
|
export interface Declaration {
|
|
@@ -66,9 +71,11 @@ export declare function shorthandLonghands(name: string): readonly string[] | un
|
|
|
66
71
|
* Whether a property inherits.
|
|
67
72
|
*
|
|
68
73
|
* @param property - Longhand
|
|
69
|
-
* @
|
|
74
|
+
* @param notInherited - Custom properties registered with `inherits: false`
|
|
75
|
+
* @returns True for inherited properties (custom properties inherit unless
|
|
76
|
+
* registered otherwise)
|
|
70
77
|
*/
|
|
71
|
-
export declare function isInherited(property: string): boolean;
|
|
78
|
+
export declare function isInherited(property: string, notInherited?: ReadonlySet<string>): boolean;
|
|
72
79
|
/**
|
|
73
80
|
* The physical longhand a logical property sets.
|
|
74
81
|
*
|
|
@@ -91,4 +98,14 @@ export declare function ownProperties(matched: Protocol.CSS.GetMatchedStylesForN
|
|
|
91
98
|
* @returns Resolution per property
|
|
92
99
|
*/
|
|
93
100
|
export declare function resolveCascade(matched: Protocol.CSS.GetMatchedStylesForNodeResponse, properties: readonly string[]): Map<string, Resolution>;
|
|
101
|
+
/**
|
|
102
|
+
* The rule a declaration is in, as written: whole when short, else its
|
|
103
|
+
* selector and that declaration (`.btn { … background-color:var(--bs-btn-bg); … }`).
|
|
104
|
+
*
|
|
105
|
+
* @param declaration - Declaration
|
|
106
|
+
* @returns `{ rule }`, or nothing for a browser, extension or attribute style
|
|
107
|
+
*/
|
|
108
|
+
export declare function ruleField(declaration: Declaration): {
|
|
109
|
+
rule?: string;
|
|
110
|
+
};
|
|
94
111
|
//# sourceMappingURL=inspectCascade.d.ts.map
|