browser-debugger-cli 0.11.0 → 0.13.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.
Files changed (222) hide show
  1. package/.claude/skills/bdg/SKILL.md +4 -4
  2. package/README.md +143 -79
  3. package/dist/commands/cdp.d.ts +22 -1
  4. package/dist/commands/cdp.js +100 -43
  5. package/dist/commands/console.d.ts +12 -0
  6. package/dist/commands/console.js +62 -12
  7. package/dist/commands/css.d.ts +13 -0
  8. package/dist/commands/css.js +53 -0
  9. package/dist/commands/dom/DomElementResolver.d.ts +3 -1
  10. package/dist/commands/dom/DomElementResolver.js +10 -3
  11. package/dist/commands/dom/a11y.js +3 -2
  12. package/dist/commands/dom/audit.d.ts +14 -0
  13. package/dist/commands/dom/audit.js +87 -0
  14. package/dist/commands/dom/eval.d.ts +3 -2
  15. package/dist/commands/dom/eval.js +11 -5
  16. package/dist/commands/dom/form.js +10 -9
  17. package/dist/commands/dom/formInteraction.js +42 -11
  18. package/dist/commands/dom/get.js +8 -8
  19. package/dist/commands/dom/helpers/index.d.ts +1 -1
  20. package/dist/commands/dom/helpers/index.js +1 -1
  21. package/dist/commands/dom/helpers/keyAttributes.d.ts +3 -2
  22. package/dist/commands/dom/helpers/keyAttributes.js +6 -4
  23. package/dist/commands/dom/helpers/query.d.ts +27 -3
  24. package/dist/commands/dom/helpers/query.js +152 -64
  25. package/dist/commands/dom/helpers/screenshot.d.ts +1 -0
  26. package/dist/commands/dom/helpers/screenshot.js +169 -49
  27. package/dist/commands/dom/index.js +7 -2
  28. package/dist/commands/dom/query.d.ts +19 -2
  29. package/dist/commands/dom/query.js +37 -6
  30. package/dist/commands/dom/screenshot.js +12 -7
  31. package/dist/commands/dom/semanticUtils.d.ts +3 -2
  32. package/dist/commands/dom/semanticUtils.js +40 -9
  33. package/dist/commands/dom/wait.js +5 -3
  34. package/dist/commands/helpJson.d.ts +82 -19
  35. package/dist/commands/helpJson.js +112 -41
  36. package/dist/commands/helpTopic.d.ts +16 -1
  37. package/dist/commands/helpTopic.js +59 -1
  38. package/dist/commands/installSkill.d.ts +15 -5
  39. package/dist/commands/installSkill.js +86 -16
  40. package/dist/commands/network/list.js +22 -12
  41. package/dist/commands/optionBehaviors.js +53 -16
  42. package/dist/commands/page.js +7 -4
  43. package/dist/commands/peek.d.ts +7 -0
  44. package/dist/commands/peek.js +65 -23
  45. package/dist/commands/shared/daemonErrorHandler.d.ts +5 -2
  46. package/dist/commands/shared/daemonErrorHandler.js +20 -9
  47. package/dist/commands/shared/dataFetcher.d.ts +12 -4
  48. package/dist/commands/shared/dataFetcher.js +12 -4
  49. package/dist/commands/shared/followMode.d.ts +9 -1
  50. package/dist/commands/shared/followMode.js +22 -4
  51. package/dist/commands/shared/optionTypes.d.ts +9 -2
  52. package/dist/commands/shared/outputFile.js +6 -1
  53. package/dist/commands/start.d.ts +20 -5
  54. package/dist/commands/start.js +84 -23
  55. package/dist/commands/stop.d.ts +11 -0
  56. package/dist/commands/stop.js +24 -1
  57. package/dist/commands/tail.d.ts +7 -1
  58. package/dist/commands/tail.js +13 -62
  59. package/dist/commands.js +2 -0
  60. package/dist/connection/cdp.d.ts +7 -0
  61. package/dist/connection/cdp.js +9 -0
  62. package/dist/connection/launcher.js +3 -2
  63. package/dist/daemon/SessionController.js +6 -1
  64. package/dist/daemon/launcher.d.ts +3 -2
  65. package/dist/daemon/launcher.js +47 -3
  66. package/dist/daemon/session/Session.d.ts +4 -1
  67. package/dist/daemon/session/Session.js +33 -2
  68. package/dist/daemon/session/TelemetryStore.d.ts +8 -1
  69. package/dist/daemon/session/TelemetryStore.js +13 -1
  70. package/dist/daemon/session/commandRegistry.js +36 -14
  71. package/dist/daemon/session/interactions.d.ts +2 -1
  72. package/dist/daemon/session/interactions.js +13 -1
  73. package/dist/daemon/session/plugins.js +19 -53
  74. package/dist/daemon/session/teardown.js +1 -1
  75. package/dist/daemon.js +9234 -7222
  76. package/dist/errors/messages.d.ts +88 -15
  77. package/dist/errors/messages.js +177 -27
  78. package/dist/index.js +19322 -13961
  79. package/dist/ipc/client.d.ts +22 -2
  80. package/dist/ipc/client.js +34 -5
  81. package/dist/ipc/protocol/auditTypes.d.ts +135 -0
  82. package/dist/ipc/protocol/auditTypes.js +6 -0
  83. package/dist/ipc/protocol/commands.d.ts +35 -0
  84. package/dist/ipc/protocol/commands.js +2 -0
  85. package/dist/ipc/protocol/domTypes.d.ts +16 -0
  86. package/dist/ipc/protocol/inspectTypes.d.ts +73 -8
  87. package/dist/ipc/session/types.d.ts +2 -0
  88. package/dist/runtime/css/search.d.ts +39 -0
  89. package/dist/runtime/css/search.js +122 -0
  90. package/dist/runtime/dom/actionEffects.d.ts +9 -2
  91. package/dist/runtime/dom/actionEffects.js +30 -14
  92. package/dist/runtime/dom/audit.d.ts +19 -0
  93. package/dist/runtime/dom/audit.js +37 -0
  94. package/dist/runtime/dom/auditModel.d.ts +45 -0
  95. package/dist/runtime/dom/auditModel.js +220 -0
  96. package/dist/runtime/dom/auditScripts.d.ts +113 -0
  97. package/dist/runtime/dom/auditScripts.js +148 -0
  98. package/dist/runtime/dom/elementGeometry.d.ts +16 -3
  99. package/dist/runtime/dom/elementGeometry.js +49 -10
  100. package/dist/runtime/dom/elementInfo.d.ts +74 -17
  101. package/dist/runtime/dom/elementInfo.js +187 -34
  102. package/dist/runtime/dom/evalHelpers.d.ts +12 -2
  103. package/dist/runtime/dom/evalHelpers.js +67 -7
  104. package/dist/runtime/dom/formDiscovery.d.ts +6 -2
  105. package/dist/runtime/dom/formDiscovery.js +20 -3
  106. package/dist/runtime/dom/formFillHelpers/fill.js +8 -12
  107. package/dist/runtime/dom/formFillHelpers/pressKey.js +2 -2
  108. package/dist/runtime/dom/formFillHelpers/shared.d.ts +16 -10
  109. package/dist/runtime/dom/formFillHelpers/shared.js +19 -52
  110. package/dist/runtime/dom/formSubmitHelpers.js +4 -3
  111. package/dist/runtime/dom/frameLayout.js +1 -0
  112. package/dist/runtime/dom/inspect.d.ts +7 -0
  113. package/dist/runtime/dom/inspect.js +92 -28
  114. package/dist/runtime/dom/inspectAllStyles.d.ts +16 -4
  115. package/dist/runtime/dom/inspectAllStyles.js +90 -7
  116. package/dist/runtime/dom/inspectCascade.d.ts +19 -2
  117. package/dist/runtime/dom/inspectCascade.js +214 -44
  118. package/dist/runtime/dom/inspectCascadeModel.d.ts +8 -0
  119. package/dist/runtime/dom/inspectCascadeModel.js +108 -34
  120. package/dist/runtime/dom/inspectHints.d.ts +26 -3
  121. package/dist/runtime/dom/inspectHints.js +125 -9
  122. package/dist/runtime/dom/inspectModel.d.ts +5 -1
  123. package/dist/runtime/dom/inspectModel.js +37 -10
  124. package/dist/runtime/dom/inspectPaintModel.d.ts +50 -22
  125. package/dist/runtime/dom/inspectPaintModel.js +182 -68
  126. package/dist/runtime/dom/inspectRules.d.ts +19 -0
  127. package/dist/runtime/dom/inspectRules.js +21 -5
  128. package/dist/runtime/dom/inspectScripts.d.ts +112 -12
  129. package/dist/runtime/dom/inspectScripts.js +357 -32
  130. package/dist/runtime/dom/inspectTree.js +10 -2
  131. package/dist/runtime/dom/inspectWhyModel.d.ts +2 -1
  132. package/dist/runtime/dom/inspectWhyModel.js +52 -10
  133. package/dist/runtime/dom/layout.js +40 -16
  134. package/dist/runtime/dom/reactEventHelpers.d.ts +21 -4
  135. package/dist/runtime/dom/reactEventHelpers.js +90 -36
  136. package/dist/runtime/dom/targetNode.d.ts +18 -5
  137. package/dist/runtime/dom/targetNode.js +268 -8
  138. package/dist/runtime/dom/wait.js +2 -1
  139. package/dist/runtime/page/bdgWorld.d.ts +57 -0
  140. package/dist/runtime/page/bdgWorld.js +180 -0
  141. package/dist/runtime/page/emulation.d.ts +13 -4
  142. package/dist/runtime/page/emulation.js +69 -4
  143. package/dist/runtime/page/replacedBuiltins.d.ts +28 -0
  144. package/dist/runtime/page/replacedBuiltins.js +136 -0
  145. package/dist/runtime/page/userAgent.d.ts +17 -0
  146. package/dist/runtime/page/userAgent.js +57 -0
  147. package/dist/session/QueryCacheManager.d.ts +4 -1
  148. package/dist/session/QueryCacheManager.js +5 -2
  149. package/dist/session/chrome.d.ts +4 -1
  150. package/dist/session/chrome.js +7 -1
  151. package/dist/session/cleanup/staleSession.d.ts +21 -4
  152. package/dist/session/cleanup/staleSession.js +79 -9
  153. package/dist/session/cleanup/userCommands.d.ts +4 -1
  154. package/dist/session/cleanup/userCommands.js +10 -5
  155. package/dist/session/daemonSocket.d.ts +10 -0
  156. package/dist/session/daemonSocket.js +22 -0
  157. package/dist/session/lastSession.d.ts +6 -3
  158. package/dist/session/lastSession.js +11 -5
  159. package/dist/session/paths.d.ts +3 -1
  160. package/dist/session/paths.js +5 -5
  161. package/dist/session/portClaims.js +4 -3
  162. package/dist/session/sessionList.d.ts +13 -5
  163. package/dist/session/sessionList.js +31 -7
  164. package/dist/telemetry/a11y.js +2 -2
  165. package/dist/telemetry/console.d.ts +2 -1
  166. package/dist/telemetry/console.js +30 -21
  167. package/dist/telemetry/pageCrash.d.ts +26 -0
  168. package/dist/telemetry/pageCrash.js +53 -0
  169. package/dist/types.d.ts +20 -0
  170. package/dist/ui/formatters/audit.d.ts +19 -0
  171. package/dist/ui/formatters/audit.js +115 -0
  172. package/dist/ui/formatters/cdp.d.ts +138 -0
  173. package/dist/ui/formatters/cdp.js +131 -0
  174. package/dist/ui/formatters/console/chronological.js +3 -1
  175. package/dist/ui/formatters/console/follow.d.ts +2 -1
  176. package/dist/ui/formatters/console/follow.js +2 -2
  177. package/dist/ui/formatters/console/json.d.ts +2 -2
  178. package/dist/ui/formatters/console/json.js +11 -5
  179. package/dist/ui/formatters/console/shared.d.ts +30 -0
  180. package/dist/ui/formatters/console/shared.js +16 -0
  181. package/dist/ui/formatters/console/summarize.d.ts +9 -2
  182. package/dist/ui/formatters/console/summarize.js +40 -9
  183. package/dist/ui/formatters/console.d.ts +2 -1
  184. package/dist/ui/formatters/console.js +7 -5
  185. package/dist/ui/formatters/details.js +3 -1
  186. package/dist/ui/formatters/dom.d.ts +2 -2
  187. package/dist/ui/formatters/dom.js +10 -8
  188. package/dist/ui/formatters/helpFormatters.js +1 -1
  189. package/dist/ui/formatters/inspect.js +50 -17
  190. package/dist/ui/formatters/installSkill.d.ts +9 -1
  191. package/dist/ui/formatters/installSkill.js +32 -6
  192. package/dist/ui/formatters/layout.js +2 -1
  193. package/dist/ui/formatters/networkList.d.ts +1 -1
  194. package/dist/ui/formatters/networkList.js +1 -2
  195. package/dist/ui/formatters/preview.d.ts +2 -0
  196. package/dist/ui/formatters/preview.js +17 -7
  197. package/dist/ui/formatters/sessions.d.ts +2 -2
  198. package/dist/ui/formatters/sessions.js +9 -2
  199. package/dist/ui/formatters/status.js +1 -1
  200. package/dist/ui/logging/logger.d.ts +1 -1
  201. package/dist/ui/messages/commands.d.ts +168 -11
  202. package/dist/ui/messages/commands.js +245 -18
  203. package/dist/ui/messages/consoleMessages.d.ts +24 -0
  204. package/dist/ui/messages/consoleMessages.js +32 -0
  205. package/dist/ui/messages/preview.d.ts +12 -0
  206. package/dist/ui/messages/preview.js +18 -2
  207. package/dist/ui/messages/session.d.ts +13 -2
  208. package/dist/ui/messages/session.js +22 -3
  209. package/dist/utils/cssValues.js +36 -4
  210. package/dist/utils/decisionTrees.js +0 -5
  211. package/dist/utils/directories.d.ts +34 -0
  212. package/dist/utils/directories.js +88 -0
  213. package/dist/utils/display.d.ts +16 -0
  214. package/dist/utils/display.js +42 -0
  215. package/dist/utils/exitCodes.d.ts +1 -0
  216. package/dist/utils/exitCodes.js +6 -0
  217. package/dist/utils/process.d.ts +12 -0
  218. package/dist/utils/process.js +25 -0
  219. package/dist/utils/suggestions.d.ts +4 -2
  220. package/dist/utils/suggestions.js +7 -5
  221. package/dist/utils/taskMappings.js +1 -1
  222. package/package.json +3 -2
@@ -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), transitions, origins without a transform,
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,9 +166,11 @@ 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',
173
+ '-webkit-text-fill-color',
150
174
  ]);
151
175
  /** SVG paint and geometry properties (noise on HTML elements) */
152
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)$/;
@@ -161,7 +185,9 @@ const CURRENT_COLOR_ECHOES = /^(border-(top|right|bottom|left)-color|outline-col
161
185
  * @returns True when it is left out
162
186
  */
163
187
  function isNoise(name, style, svg) {
164
- if (name.startsWith('--') || NOISE.has(name) || name.startsWith('transition'))
188
+ if (name.startsWith('--') || NOISE.has(name))
189
+ return true;
190
+ if (name.startsWith('transition') && style['transition-duration'] === '0s')
165
191
  return true;
166
192
  if (LOGICAL.test(name) && !name.endsWith('-axis'))
167
193
  return true;
@@ -266,7 +292,56 @@ const COLLAPSES = [
266
292
  ],
267
293
  write: (v) => v.join(' '),
268
294
  },
295
+ {
296
+ shorthand: 'transition',
297
+ longhands: [
298
+ 'transition-property',
299
+ 'transition-duration',
300
+ 'transition-timing-function',
301
+ 'transition-delay',
302
+ 'transition-behavior',
303
+ ],
304
+ write: transitionList,
305
+ },
269
306
  ];
307
+ /**
308
+ * A transition list from its longhands' comma lists, one transition per
309
+ * property, defaults left out (`background-color 0.25s ease-in, opacity 0.2s`).
310
+ *
311
+ * @param values - Property, duration, timing function, delay and behavior lists
312
+ * @returns Transition shorthand
313
+ */
314
+ function transitionList(values) {
315
+ const lists = values.map((value) => value.split(/,\s*(?![^(]*\))/));
316
+ const [properties = []] = lists;
317
+ return properties
318
+ .map((property, i) => {
319
+ const at = (list) => list?.[i % list.length] ?? '';
320
+ const [duration, timing, delay, behavior] = lists.slice(1).map(at);
321
+ return [
322
+ property,
323
+ duration,
324
+ timing === 'ease' ? '' : timing,
325
+ delay === '0s' ? '' : delay,
326
+ behavior === 'normal' ? '' : behavior,
327
+ ]
328
+ .filter(Boolean)
329
+ .join(' ');
330
+ })
331
+ .join(', ');
332
+ }
333
+ /**
334
+ * A shorthand's computed value as `--all` writes it (`margin 0 8`,
335
+ * `outline 2 solid #f00`, `transition color 1s ease-in`).
336
+ *
337
+ * @param shorthand - Shorthand name
338
+ * @param style - Computed longhands
339
+ * @returns Value, or undefined for a shorthand `--all` does not collapse
340
+ */
341
+ export function collapsedValue(shorthand, style) {
342
+ const group = COLLAPSES.find((entry) => entry.shorthand === shorthand);
343
+ return group?.write(group.longhands.map((longhand) => normalizeProperty(longhand, style[longhand] ?? '')));
344
+ }
270
345
  /**
271
346
  * Border longhands collapsed: one `border` when the four drawn sides are
272
347
  * equal, else one entry per drawn side.
@@ -337,13 +412,15 @@ function isUnsetOffset(name, value, style) {
337
412
  *
338
413
  * @param style - Computed styles
339
414
  * @param svg - The element is an SVG element (SVG properties are listed)
415
+ * @param formControl - A form control (`appearance: none` is not its default)
340
416
  * @returns Properties and values
341
417
  */
342
- export function allStyles(style, svg) {
418
+ export function allStyles(style, svg, formControl = false) {
343
419
  const kept = new Map();
344
420
  const transformed = (style['transform'] ?? 'none') !== 'none';
345
421
  for (const [name, value] of Object.entries(style)) {
346
- if (name === 'transform-origin' && transformed) {
422
+ const set = (name === 'transform-origin' && transformed) || (name === 'appearance' && formControl);
423
+ if (set) {
347
424
  kept.set(name, normalizeProperty(name, value));
348
425
  continue;
349
426
  }
@@ -364,10 +441,12 @@ export function allStyles(style, svg) {
364
441
  * @param style - Computed longhands (CDP)
365
442
  * @param pageValues - Values the page read
366
443
  * @param pageUnknown - Names the page does not know as CSS properties
444
+ * @param rootFontSize - Root font size (px): a custom property that is one
445
+ * rem length is also given in px
367
446
  * @returns Values by name (an unset custom property is empty), and the
368
447
  * names that are not properties
369
448
  */
370
- export function selectedProps(names, style, pageValues, pageUnknown = []) {
449
+ export function selectedProps(names, style, pageValues, pageUnknown = [], rootFontSize = 16) {
371
450
  const props = {};
372
451
  const unknown = [];
373
452
  for (const name of names) {
@@ -377,7 +456,11 @@ export function selectedProps(names, style, pageValues, pageUnknown = []) {
377
456
  unknown.push(name);
378
457
  }
379
458
  else {
380
- props[name] = { computed, value: normalizeProperty(name, computed) };
459
+ const rem = name.startsWith('--') ? /^(-?\d*\.?\d+)rem$/.exec(computed.trim()) : null;
460
+ const value = rem
461
+ ? String(Math.round(Number(rem[1]) * rootFontSize * 10) / 10)
462
+ : normalizeProperty(name, computed);
463
+ props[name] = { computed, value };
381
464
  }
382
465
  }
383
466
  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
- * @returns True for inherited properties (custom properties inherit)
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
@@ -13,6 +13,7 @@
13
13
  * writing). With no declaration of its own, an inherited property comes from
14
14
  * the nearest ancestor that declares it.
15
15
  */
16
+ import { truncateByLength } from '../../utils/strings.js';
16
17
  /**
17
18
  * The four side longhands of a pattern.
18
19
  *
@@ -64,9 +65,37 @@ const SHORTHANDS = {
64
65
  `border-${side}`,
65
66
  ['width', 'style', 'color'].map((part) => `border-${side}-${part}`),
66
67
  ])),
67
- background: ['background-color', 'background-image'],
68
+ background: [
69
+ 'background-color',
70
+ 'background-image',
71
+ 'background-position-x',
72
+ 'background-position-y',
73
+ 'background-size',
74
+ 'background-repeat',
75
+ 'background-origin',
76
+ 'background-clip',
77
+ 'background-attachment',
78
+ ],
68
79
  font: ['font-style', 'font-weight', 'font-size', 'line-height', 'font-family'],
69
80
  flex: ['flex-grow', 'flex-shrink', 'flex-basis'],
81
+ 'flex-flow': ['flex-direction', 'flex-wrap'],
82
+ outline: ['outline-width', 'outline-style', 'outline-color'],
83
+ 'text-decoration': [
84
+ 'text-decoration-line',
85
+ 'text-decoration-style',
86
+ 'text-decoration-color',
87
+ 'text-decoration-thickness',
88
+ ],
89
+ transition: [
90
+ 'transition-property',
91
+ 'transition-duration',
92
+ 'transition-timing-function',
93
+ 'transition-delay',
94
+ 'transition-behavior',
95
+ ],
96
+ 'background-position': ['background-position-x', 'background-position-y'],
97
+ 'list-style': ['list-style-position', 'list-style-image', 'list-style-type'],
98
+ columns: ['column-width', 'column-count'],
70
99
  'place-items': ['align-items', 'justify-items'],
71
100
  'place-content': ['align-content', 'justify-content'],
72
101
  'place-self': ['align-self', 'justify-self'],
@@ -91,35 +120,31 @@ const LOGICAL_SIDES = {
91
120
  'inline-start': 'left',
92
121
  'inline-end': 'right',
93
122
  };
94
- /** Properties that inherit (those `dom inspect` reports) */
123
+ /** Longhands that inherit (CSS, SVG and the prefixed ones Chrome computes) */
95
124
  const INHERITED = new Set([
96
- 'color',
97
- 'font-family',
98
- 'font-size',
99
- 'font-style',
100
- 'font-weight',
101
- 'font-variant',
102
- 'font-feature-settings',
103
- 'font-variation-settings',
104
- 'line-height',
105
- 'letter-spacing',
106
- 'word-spacing',
107
- 'text-align',
108
- 'text-indent',
109
- 'text-transform',
110
- 'text-shadow',
111
- 'white-space-collapse',
112
- 'text-wrap-mode',
113
- 'visibility',
114
- 'cursor',
115
- 'direction',
116
- 'list-style-type',
117
- 'list-style-position',
118
- 'tab-size',
119
- 'pointer-events',
120
- 'user-select',
121
- '-webkit-text-security',
122
- ]);
125
+ 'color caret-color accent-color color-scheme forced-color-adjust print-color-adjust',
126
+ 'font-family font-size font-style font-weight font-stretch font-size-adjust font-kerning',
127
+ 'font-optical-sizing font-palette font-language-override font-feature-settings',
128
+ 'font-variation-settings font-variant font-variant-caps font-variant-ligatures',
129
+ 'font-variant-numeric font-variant-east-asian font-variant-alternates',
130
+ 'font-variant-position font-variant-emoji font-synthesis-weight font-synthesis-style',
131
+ 'font-synthesis-small-caps line-height letter-spacing word-spacing text-align',
132
+ 'text-align-last text-indent text-justify text-transform text-shadow text-rendering',
133
+ 'text-size-adjust text-underline-position text-underline-offset text-decoration-skip-ink',
134
+ 'text-emphasis-style text-emphasis-color text-emphasis-position text-orientation',
135
+ 'text-wrap-mode text-wrap-style text-spacing-trim text-autospace white-space-collapse',
136
+ 'word-break overflow-wrap line-break hyphens hyphenate-character hyphenate-limit-chars',
137
+ 'tab-size direction writing-mode ruby-position ruby-align visibility cursor',
138
+ 'pointer-events user-select interpolate-size list-style-type list-style-position',
139
+ 'list-style-image quotes border-collapse border-spacing caption-side empty-cells orphans',
140
+ 'widows image-rendering fill fill-opacity fill-rule stroke stroke-width stroke-opacity',
141
+ 'stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit',
142
+ 'paint-order marker-start marker-mid marker-end clip-rule color-interpolation',
143
+ 'color-interpolation-filters color-rendering shape-rendering text-anchor dominant-baseline',
144
+ 'math-depth math-style math-shift scrollbar-color text-combine-upright',
145
+ '-webkit-text-security -webkit-text-fill-color',
146
+ '-webkit-text-stroke-color -webkit-text-stroke-width',
147
+ ].flatMap((names) => names.split(' ')));
123
148
  /**
124
149
  * The longhands of a shorthand.
125
150
  *
@@ -133,10 +158,30 @@ export function shorthandLonghands(name) {
133
158
  * Whether a property inherits.
134
159
  *
135
160
  * @param property - Longhand
136
- * @returns True for inherited properties (custom properties inherit)
161
+ * @param notInherited - Custom properties registered with `inherits: false`
162
+ * @returns True for inherited properties (custom properties inherit unless
163
+ * registered otherwise)
164
+ */
165
+ export function isInherited(property, notInherited) {
166
+ if (property.startsWith('--'))
167
+ return !notInherited?.has(property);
168
+ return INHERITED.has(property);
169
+ }
170
+ /**
171
+ * Custom properties registered not to inherit, with `@property` or
172
+ * `CSS.registerProperty`.
173
+ *
174
+ * @param matched - `CSS.getMatchedStylesForNode` response
175
+ * @returns Their names
137
176
  */
138
- export function isInherited(property) {
139
- return INHERITED.has(property) || property.startsWith('--');
177
+ function nonInheritingProperties(matched) {
178
+ const fromRules = (matched.cssPropertyRules ?? [])
179
+ .filter((rule) => rule.style.cssProperties.some((property) => property.name === 'inherits' && property.value.trim() === 'false'))
180
+ .map((rule) => rule.propertyName.text);
181
+ const fromScript = (matched.cssPropertyRegistrations ?? [])
182
+ .filter((registration) => !registration.inherits)
183
+ .map((registration) => registration.propertyName);
184
+ return new Set([...fromRules, ...fromScript]);
140
185
  }
141
186
  /**
142
187
  * The physical longhand a logical property sets.
@@ -211,23 +256,43 @@ function declarationsOf(style, source) {
211
256
  });
212
257
  }
213
258
  /**
214
- * Source of a matched rule: its matching selector, origin, stylesheet,
215
- * layer (nested ones as `outer.inner`), the media or container condition
216
- * it is under (`not all and (…)`, as Chrome writes `not (…)`, shortened) and
217
- * the selector's specificity.
259
+ * The selector of a rule's list that matched with the highest specificity
260
+ * (the one the cascade uses: `.title` of `h1, .title`).
261
+ *
262
+ * @param match - CDP rule match
263
+ * @returns Selector
264
+ */
265
+ function strongestMatchingSelector(match) {
266
+ const weight = (value) => {
267
+ const s = value?.specificity;
268
+ return s ? s.a * 1e6 + s.b * 1e3 + s.c : -1;
269
+ };
270
+ const selectors = match.rule.selectorList.selectors;
271
+ const matching = match.matchingSelectors.map((index) => selectors[index]);
272
+ return (matching.reduce((best, value) => (weight(value) > weight(best) ? value : best), matching[0]) ??
273
+ selectors[0]);
274
+ }
275
+ /**
276
+ * Source of a matched rule: its matching selector (the strongest of a list),
277
+ * origin, stylesheet, layer (nested ones as `outer.inner`), the media,
278
+ * container, `@supports` and `@scope` conditions it is under (`not all and
279
+ * (…)`, as Chrome writes `not (…)`, shortened) and the selector's specificity.
218
280
  *
219
281
  * @param match - CDP rule match
220
282
  * @returns Declaration source
221
283
  */
222
284
  function ruleSource(match) {
223
285
  const { rule } = match;
224
- const matching = rule.selectorList.selectors[match.matchingSelectors[0] ?? 0];
286
+ const matching = strongestMatchingSelector(match);
225
287
  const selector = matching?.text ?? rule.selectorList.text;
226
288
  const layer = rule.layers?.map((l) => l.text || '(anonymous)').join('.');
227
- const condition = [...(rule.media ?? []), ...(rule.containerQueries ?? [])]
228
- .map((c) => c.text.replace(/^not all and /, 'not '))
229
- .join(' and ');
289
+ const condition = [
290
+ ...[...(rule.media ?? []), ...(rule.containerQueries ?? [])].map((c) => c.text.replace(/^not all and /, 'not ')),
291
+ ...(rule.supports ?? []).map((c) => `supports ${c.text}`),
292
+ ...(rule.scopes ?? []).map((c) => `scope ${c.text}`),
293
+ ].join(' and ');
230
294
  const specificity = matching?.specificity;
295
+ const declarations = rule.style.cssText;
231
296
  return {
232
297
  kind: 'rule',
233
298
  selector,
@@ -236,6 +301,9 @@ function ruleSource(match) {
236
301
  ...(rule.styleSheetId && { styleSheetId: rule.styleSheetId }),
237
302
  ...(layer && { layer }),
238
303
  ...(condition && { condition }),
304
+ ...(declarations && {
305
+ rule: { selector: collapse(rule.selectorList.text), declarations: collapse(declarations) },
306
+ }),
239
307
  };
240
308
  }
241
309
  /**
@@ -248,9 +316,23 @@ function orderedDeclarations(entry) {
248
316
  return [
249
317
  ...declarationsOf(entry.attributesStyle, { kind: 'attribute' }),
250
318
  ...(entry.matchedCSSRules ?? []).flatMap((match) => declarationsOf(match.rule.style, ruleSource(match))),
251
- ...declarationsOf(entry.inlineStyle, { kind: 'inline' }),
319
+ ...declarationsOf(entry.inlineStyle, {
320
+ kind: 'inline',
321
+ ...(entry.inlineStyle?.cssText && {
322
+ rule: { declarations: collapse(entry.inlineStyle.cssText) },
323
+ }),
324
+ }),
252
325
  ];
253
326
  }
327
+ /**
328
+ * Text with runs of whitespace collapsed to one space.
329
+ *
330
+ * @param text - CSS text
331
+ * @returns Collapsed, trimmed
332
+ */
333
+ function collapse(text) {
334
+ return text.replace(/\s+/g, ' ').trim();
335
+ }
254
336
  /**
255
337
  * Whether a declaration comes from the page's authors (not the browser's
256
338
  * own stylesheet or an extension's).
@@ -337,6 +419,7 @@ export function ownProperties(matched) {
337
419
  export function resolveCascade(matched, properties) {
338
420
  const own = orderedDeclarations(matched);
339
421
  const ancestors = (matched.inherited ?? []).map(orderedDeclarations);
422
+ const notInherited = nonInheritingProperties(matched);
340
423
  const resolved = new Map();
341
424
  for (const property of properties) {
342
425
  const ranked = byPrecedence(own.filter((d) => d.property === property));
@@ -345,7 +428,7 @@ export function resolveCascade(matched, properties) {
345
428
  resolved.set(property, { winner, overridden });
346
429
  continue;
347
430
  }
348
- resolved.set(property, inheritedWinner(property, ancestors));
431
+ resolved.set(property, inheritedWinner(property, ancestors, notInherited));
349
432
  }
350
433
  return resolved;
351
434
  }
@@ -354,11 +437,12 @@ export function resolveCascade(matched, properties) {
354
437
  *
355
438
  * @param property - Longhand
356
439
  * @param ancestors - Declarations of each ancestor, nearest first
440
+ * @param notInherited - Custom properties registered not to inherit
357
441
  * @returns The winner and the declarations it beat on that ancestor (none for
358
442
  * non-inherited properties or when no ancestor sets it)
359
443
  */
360
- function inheritedWinner(property, ancestors) {
361
- if (!isInherited(property))
444
+ function inheritedWinner(property, ancestors, notInherited) {
445
+ if (!isInherited(property, notInherited))
362
446
  return { overridden: [] };
363
447
  for (const [depth, declarations] of ancestors.entries()) {
364
448
  const ranked = byPrecedence(declarations.filter((d) => d.property === property)).map((declaration) => ({ ...declaration, ancestor: depth + 1 }));
@@ -368,4 +452,90 @@ function inheritedWinner(property, ancestors) {
368
452
  }
369
453
  return { overridden: [] };
370
454
  }
455
+ /** Longest rule given whole; a longer one (minified CSS) is cut to the declaration */
456
+ const RULE_TEXT_LENGTH = 300;
457
+ /**
458
+ * The rule a declaration is in, as written: whole when short, else its
459
+ * selector and that declaration (`.btn { … background-color:var(--bs-btn-bg); … }`).
460
+ *
461
+ * @param declaration - Declaration
462
+ * @returns `{ rule }`, or nothing for a browser, extension or attribute style
463
+ */
464
+ export function ruleField(declaration) {
465
+ const { rule } = declaration.source;
466
+ if (!rule || !isAuthor(declaration))
467
+ return {};
468
+ const whole = ruleText(rule.selector, rule.declarations);
469
+ if (whole.length <= RULE_TEXT_LENGTH)
470
+ return { rule: whole };
471
+ const own = lastDeclarationOf(rule.declarations, declaration.via ?? declaration.property);
472
+ if (!own)
473
+ return { rule: truncateByLength(whole, RULE_TEXT_LENGTH) };
474
+ const selector = rule.selector === undefined ? undefined : truncateByLength(rule.selector, 80);
475
+ return { rule: ruleText(selector, `… ${truncateByLength(own, RULE_TEXT_LENGTH - 100)}; …`) };
476
+ }
477
+ /**
478
+ * A rule's text: `selector { declarations }`, or `style="declarations"`.
479
+ *
480
+ * @param selector - Selector (absent: the style attribute)
481
+ * @param declarations - Declarations
482
+ * @returns Text
483
+ */
484
+ function ruleText(selector, declarations) {
485
+ return selector === undefined
486
+ ? `style="${declarations.replaceAll('"', '\\"')}"`
487
+ : `${selector} { ${declarations} }`;
488
+ }
489
+ /**
490
+ * The last declaration of a property in a declaration block (the one that
491
+ * counts when it is repeated), split at semicolons outside strings and
492
+ * parentheses (`url(data:image/png;base64,…)` stays whole).
493
+ *
494
+ * @param declarations - Declaration block
495
+ * @param property - Property as written (custom properties are case-sensitive)
496
+ * @returns The declaration, without its semicolon
497
+ */
498
+ function lastDeclarationOf(declarations, property) {
499
+ const name = (part) => {
500
+ const raw = part.slice(0, part.indexOf(':')).trim();
501
+ return raw.startsWith('--') ? raw : raw.toLowerCase();
502
+ };
503
+ const wanted = property.startsWith('--') ? property : property.toLowerCase();
504
+ return splitDeclarations(declarations)
505
+ .filter((part) => part.includes(':') && name(part) === wanted)
506
+ .at(-1);
507
+ }
508
+ /**
509
+ * Split a declaration block at its top-level semicolons.
510
+ *
511
+ * @param block - Declarations
512
+ * @returns Declarations, trimmed, empty ones dropped
513
+ */
514
+ function splitDeclarations(block) {
515
+ const parts = [];
516
+ let depth = 0;
517
+ let quote;
518
+ let start = 0;
519
+ for (let i = 0; i < block.length; i++) {
520
+ const char = block[i];
521
+ if (quote) {
522
+ if (char === '\\')
523
+ i++;
524
+ else if (char === quote)
525
+ quote = undefined;
526
+ }
527
+ else if (char === '"' || char === "'")
528
+ quote = char;
529
+ else if (char === '(')
530
+ depth++;
531
+ else if (char === ')')
532
+ depth = Math.max(0, depth - 1);
533
+ else if (char === ';' && depth === 0) {
534
+ parts.push(block.slice(start, i));
535
+ start = i + 1;
536
+ }
537
+ }
538
+ parts.push(block.slice(start));
539
+ return parts.map((part) => part.trim()).filter(Boolean);
540
+ }
371
541
  //# sourceMappingURL=inspectCascade.js.map
@@ -8,6 +8,7 @@ import type { Protocol } from '../../connection/typed-cdp.js';
8
8
  import type { InspectResult } from '../../ipc/protocol/inspectTypes.js';
9
9
  import { type Declaration } from './inspectCascade.js';
10
10
  import type { StyleMap } from './inspectLayoutModel.js';
11
+ import type { InactiveRule } from './inspectScripts.js';
11
12
  /** Longhands reported together, so a shorthand can stand for its sides */
12
13
  export interface PropertyGroup {
13
14
  shorthand?: string;
@@ -25,8 +26,15 @@ export interface CascadeInput {
25
26
  why?: string;
26
27
  /** `--props` names: `--rules` then covers only these */
27
28
  props?: string[];
29
+ /** Longhands of the `--why` shorthand as the browser expands it */
30
+ whyLonghands?: string[];
31
+ /** Computed value of the `--why` shorthand, as the page writes it */
32
+ whyComputed?: string;
33
+ /** Rules that set the `--why` property under a condition that does not apply now */
34
+ whyInactive?: InactiveRule[];
28
35
  /** Check for declarations that have no effect (default true) */
29
36
  hints?: boolean;
37
+ /** A form control that draws text (its font is checked against the parent's) */
30
38
  formControl?: boolean;
31
39
  }
32
40
  /**