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.
- package/.claude/skills/bdg/SKILL.md +4 -4
- package/README.md +143 -79
- 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 +62 -12
- package/dist/commands/css.d.ts +13 -0
- package/dist/commands/css.js +53 -0
- package/dist/commands/dom/DomElementResolver.d.ts +3 -1
- package/dist/commands/dom/DomElementResolver.js +10 -3
- package/dist/commands/dom/a11y.js +3 -2
- package/dist/commands/dom/audit.d.ts +14 -0
- package/dist/commands/dom/audit.js +87 -0
- package/dist/commands/dom/eval.d.ts +3 -2
- package/dist/commands/dom/eval.js +11 -5
- package/dist/commands/dom/form.js +10 -9
- package/dist/commands/dom/formInteraction.js +42 -11
- package/dist/commands/dom/get.js +8 -8
- package/dist/commands/dom/helpers/index.d.ts +1 -1
- package/dist/commands/dom/helpers/index.js +1 -1
- package/dist/commands/dom/helpers/keyAttributes.d.ts +3 -2
- package/dist/commands/dom/helpers/keyAttributes.js +6 -4
- 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.d.ts +1 -0
- package/dist/commands/dom/helpers/screenshot.js +169 -49
- package/dist/commands/dom/index.js +7 -2
- package/dist/commands/dom/query.d.ts +19 -2
- package/dist/commands/dom/query.js +37 -6
- package/dist/commands/dom/screenshot.js +12 -7
- package/dist/commands/dom/semanticUtils.d.ts +3 -2
- package/dist/commands/dom/semanticUtils.js +40 -9
- package/dist/commands/dom/wait.js +5 -3
- 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 +22 -12
- package/dist/commands/optionBehaviors.js +53 -16
- 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/daemonErrorHandler.d.ts +5 -2
- package/dist/commands/shared/daemonErrorHandler.js +20 -9
- package/dist/commands/shared/dataFetcher.d.ts +12 -4
- package/dist/commands/shared/dataFetcher.js +12 -4
- package/dist/commands/shared/followMode.d.ts +9 -1
- package/dist/commands/shared/followMode.js +22 -4
- package/dist/commands/shared/optionTypes.d.ts +9 -2
- package/dist/commands/shared/outputFile.js +6 -1
- package/dist/commands/start.d.ts +20 -5
- package/dist/commands/start.js +84 -23
- package/dist/commands/stop.d.ts +11 -0
- package/dist/commands/stop.js +24 -1
- package/dist/commands/tail.d.ts +7 -1
- package/dist/commands/tail.js +13 -62
- package/dist/commands.js +2 -0
- package/dist/connection/cdp.d.ts +7 -0
- package/dist/connection/cdp.js +9 -0
- package/dist/connection/launcher.js +3 -2
- package/dist/daemon/SessionController.js +6 -1
- package/dist/daemon/launcher.d.ts +3 -2
- package/dist/daemon/launcher.js +47 -3
- package/dist/daemon/session/Session.d.ts +4 -1
- package/dist/daemon/session/Session.js +33 -2
- package/dist/daemon/session/TelemetryStore.d.ts +8 -1
- package/dist/daemon/session/TelemetryStore.js +13 -1
- package/dist/daemon/session/commandRegistry.js +36 -14
- package/dist/daemon/session/interactions.d.ts +2 -1
- package/dist/daemon/session/interactions.js +13 -1
- package/dist/daemon/session/plugins.js +19 -53
- package/dist/daemon/session/teardown.js +1 -1
- package/dist/daemon.js +9234 -7222
- package/dist/errors/messages.d.ts +88 -15
- package/dist/errors/messages.js +177 -27
- package/dist/index.js +19322 -13961
- package/dist/ipc/client.d.ts +22 -2
- package/dist/ipc/client.js +34 -5
- package/dist/ipc/protocol/auditTypes.d.ts +135 -0
- package/dist/ipc/protocol/auditTypes.js +6 -0
- package/dist/ipc/protocol/commands.d.ts +35 -0
- package/dist/ipc/protocol/commands.js +2 -0
- package/dist/ipc/protocol/domTypes.d.ts +16 -0
- package/dist/ipc/protocol/inspectTypes.d.ts +73 -8
- package/dist/ipc/session/types.d.ts +2 -0
- package/dist/runtime/css/search.d.ts +39 -0
- package/dist/runtime/css/search.js +122 -0
- package/dist/runtime/dom/actionEffects.d.ts +9 -2
- package/dist/runtime/dom/actionEffects.js +30 -14
- package/dist/runtime/dom/audit.d.ts +19 -0
- package/dist/runtime/dom/audit.js +37 -0
- package/dist/runtime/dom/auditModel.d.ts +45 -0
- package/dist/runtime/dom/auditModel.js +220 -0
- package/dist/runtime/dom/auditScripts.d.ts +113 -0
- package/dist/runtime/dom/auditScripts.js +148 -0
- package/dist/runtime/dom/elementGeometry.d.ts +16 -3
- package/dist/runtime/dom/elementGeometry.js +49 -10
- package/dist/runtime/dom/elementInfo.d.ts +74 -17
- package/dist/runtime/dom/elementInfo.js +187 -34
- 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 +8 -12
- 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/inspect.d.ts +7 -0
- package/dist/runtime/dom/inspect.js +92 -28
- package/dist/runtime/dom/inspectAllStyles.d.ts +16 -4
- package/dist/runtime/dom/inspectAllStyles.js +90 -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 +5 -1
- package/dist/runtime/dom/inspectModel.js +37 -10
- package/dist/runtime/dom/inspectPaintModel.d.ts +50 -22
- package/dist/runtime/dom/inspectPaintModel.js +182 -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 +112 -12
- package/dist/runtime/dom/inspectScripts.js +357 -32
- 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 +40 -16
- package/dist/runtime/dom/reactEventHelpers.d.ts +21 -4
- package/dist/runtime/dom/reactEventHelpers.js +90 -36
- package/dist/runtime/dom/targetNode.d.ts +18 -5
- package/dist/runtime/dom/targetNode.js +268 -8
- 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/emulation.d.ts +13 -4
- package/dist/runtime/page/emulation.js +69 -4
- package/dist/runtime/page/replacedBuiltins.d.ts +28 -0
- package/dist/runtime/page/replacedBuiltins.js +136 -0
- package/dist/runtime/page/userAgent.d.ts +17 -0
- package/dist/runtime/page/userAgent.js +57 -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.js +2 -2
- package/dist/telemetry/console.d.ts +2 -1
- package/dist/telemetry/console.js +30 -21
- package/dist/telemetry/pageCrash.d.ts +26 -0
- package/dist/telemetry/pageCrash.js +53 -0
- package/dist/types.d.ts +20 -0
- package/dist/ui/formatters/audit.d.ts +19 -0
- package/dist/ui/formatters/audit.js +115 -0
- package/dist/ui/formatters/cdp.d.ts +138 -0
- package/dist/ui/formatters/cdp.js +131 -0
- package/dist/ui/formatters/console/chronological.js +3 -1
- package/dist/ui/formatters/console/follow.d.ts +2 -1
- package/dist/ui/formatters/console/follow.js +2 -2
- package/dist/ui/formatters/console/json.d.ts +2 -2
- package/dist/ui/formatters/console/json.js +11 -5
- package/dist/ui/formatters/console/shared.d.ts +30 -0
- package/dist/ui/formatters/console/shared.js +16 -0
- package/dist/ui/formatters/console/summarize.d.ts +9 -2
- package/dist/ui/formatters/console/summarize.js +40 -9
- package/dist/ui/formatters/console.d.ts +2 -1
- package/dist/ui/formatters/console.js +7 -5
- package/dist/ui/formatters/details.js +3 -1
- package/dist/ui/formatters/dom.d.ts +2 -2
- package/dist/ui/formatters/dom.js +10 -8
- package/dist/ui/formatters/helpFormatters.js +1 -1
- package/dist/ui/formatters/inspect.js +50 -17
- package/dist/ui/formatters/installSkill.d.ts +9 -1
- package/dist/ui/formatters/installSkill.js +32 -6
- package/dist/ui/formatters/layout.js +2 -1
- package/dist/ui/formatters/networkList.d.ts +1 -1
- package/dist/ui/formatters/networkList.js +1 -2
- package/dist/ui/formatters/preview.d.ts +2 -0
- package/dist/ui/formatters/preview.js +17 -7
- package/dist/ui/formatters/sessions.d.ts +2 -2
- package/dist/ui/formatters/sessions.js +9 -2
- package/dist/ui/formatters/status.js +1 -1
- package/dist/ui/logging/logger.d.ts +1 -1
- package/dist/ui/messages/commands.d.ts +168 -11
- package/dist/ui/messages/commands.js +245 -18
- package/dist/ui/messages/consoleMessages.d.ts +24 -0
- package/dist/ui/messages/consoleMessages.js +32 -0
- package/dist/ui/messages/preview.d.ts +12 -0
- package/dist/ui/messages/preview.js +18 -2
- package/dist/ui/messages/session.d.ts +13 -2
- package/dist/ui/messages/session.js +22 -3
- package/dist/utils/cssValues.js +36 -4
- package/dist/utils/decisionTrees.js +0 -5
- 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/process.d.ts +12 -0
- package/dist/utils/process.js +25 -0
- 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
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* Discovers forms, extracts semantic labels, detects state and validation,
|
|
5
5
|
* and returns structured data for agent consumption.
|
|
6
6
|
*/
|
|
7
|
+
import { MASKED_VALUE, SENSITIVE_FIELD_JS } from './elementInfo.js';
|
|
7
8
|
/**
|
|
8
9
|
* Page-context script for form discovery.
|
|
9
10
|
*
|
|
@@ -11,7 +12,11 @@
|
|
|
11
12
|
* - Native form elements and inputs
|
|
12
13
|
* - Custom components with ARIA roles
|
|
13
14
|
* - Labels via priority chain (label[for], aria-label, placeholder, etc.)
|
|
14
|
-
* - Current values and validation state
|
|
15
|
+
* - Current values and validation state (never a secret: a hidden input's
|
|
16
|
+
* value is left out, a sensitive text field's or select's
|
|
17
|
+
* ({@link SENSITIVE_FIELD_JS}; checkboxes and radios named like one keep
|
|
18
|
+
* their checked state) is {@link MASKED_VALUE} when filled, and a
|
|
19
|
+
* sensitive select's options do not say which is chosen)
|
|
15
20
|
* - Form relevance scoring for multi-form pages
|
|
16
21
|
*/
|
|
17
22
|
export const FORM_DISCOVERY_SCRIPT = `
|
|
@@ -206,6 +211,10 @@ export const FORM_DISCOVERY_SCRIPT = `
|
|
|
206
211
|
function getFieldValue(element) {
|
|
207
212
|
const tag = element.tagName.toLowerCase();
|
|
208
213
|
const type = element.type?.toLowerCase() || 'text';
|
|
214
|
+
if (type === 'hidden') return '';
|
|
215
|
+
if (isSecret(element)) {
|
|
216
|
+
return (element.isContentEditable ? element.textContent : element.value) ? '${MASKED_VALUE}' : '';
|
|
217
|
+
}
|
|
209
218
|
if (tag === 'select') {
|
|
210
219
|
if (element.multiple) {
|
|
211
220
|
return Array.from(element.selectedOptions).map(o => o.value);
|
|
@@ -265,12 +274,20 @@ export const FORM_DISCOVERY_SCRIPT = `
|
|
|
265
274
|
return tag === 'input' || tag === 'textarea' || tag === 'select';
|
|
266
275
|
}
|
|
267
276
|
|
|
277
|
+
function isSecret(element) {
|
|
278
|
+
const type = element.type?.toLowerCase() || 'text';
|
|
279
|
+
if (type === 'checkbox' || type === 'radio') return false;
|
|
280
|
+
if (element.getAttribute('role') === 'checkbox' || element.getAttribute('role') === 'switch') return false;
|
|
281
|
+
return (${SENSITIVE_FIELD_JS})(element);
|
|
282
|
+
}
|
|
283
|
+
|
|
268
284
|
function getSelectOptions(element) {
|
|
269
285
|
if (element.tagName.toLowerCase() !== 'select') return undefined;
|
|
286
|
+
const secret = isSecret(element);
|
|
270
287
|
return Array.from(element.options).map(opt => ({
|
|
271
|
-
value: opt.value,
|
|
288
|
+
value: secret ? '' : opt.value,
|
|
272
289
|
label: opt.textContent.trim(),
|
|
273
|
-
selected: opt.selected
|
|
290
|
+
selected: secret ? false : opt.selected
|
|
274
291
|
}));
|
|
275
292
|
}
|
|
276
293
|
|
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
import * as fs from 'node:fs';
|
|
6
6
|
import * as path from 'node:path';
|
|
7
7
|
import { CommandError } from '../../../errors/index.js';
|
|
8
|
-
import { fileNotFoundError, uploadDirectoryError, singleFileInputError, fillableElementNotFoundError,
|
|
9
|
-
import { escapeValueForJS,
|
|
10
|
-
import { FILL_READ_BACK_SCRIPT, REACT_FILL_SCRIPT, CLICK_ELEMENT_SCRIPT, isFillResult, isClickResult, } from '../reactEventHelpers.js';
|
|
8
|
+
import { fileNotFoundError, uploadDirectoryError, singleFileInputError, fillableElementNotFoundError, clickTargetDetachedError, unexpectedResponseFormatError, operationFailedError, pressNotReceivedError, unreachableElementError, } from '../../../errors/messages.js';
|
|
9
|
+
import { ActionScriptError, escapeValueForJS, exceptionSummary, throwIfInvalidSelector, withMultipleMatchesWarning, withValueMismatchWarning, } from './shared.js';
|
|
10
|
+
import { FILL_READ_BACK_SCRIPT, FIRE_EVENT_JS, 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';
|
|
@@ -32,9 +32,7 @@ export async function fillElement(cdp, selector, value, options = {}) {
|
|
|
32
32
|
const cdpResponse = response;
|
|
33
33
|
if (cdpResponse.exceptionDetails) {
|
|
34
34
|
throwIfInvalidSelector(cdpResponse.exceptionDetails, selector);
|
|
35
|
-
|
|
36
|
-
const err = fillableElementNotFoundError(selector);
|
|
37
|
-
throw new CommandError(errorMessage, { suggestion: err.suggestion }, EXIT_CODES.SOFTWARE_ERROR);
|
|
35
|
+
throw new ActionScriptError('fill', exceptionSummary(cdpResponse.exceptionDetails), selector);
|
|
38
36
|
}
|
|
39
37
|
if (cdpResponse.result?.value && isFillResult(cdpResponse.result.value)) {
|
|
40
38
|
const result = cdpResponse.result.value;
|
|
@@ -106,8 +104,8 @@ function isValueMismatch(value) {
|
|
|
106
104
|
/** Empties a file input as a user removing the selection would (with events). */
|
|
107
105
|
const CLEAR_FILE_INPUT_FUNCTION = `function () {
|
|
108
106
|
this.value = '';
|
|
109
|
-
this
|
|
110
|
-
this
|
|
107
|
+
(${FIRE_EVENT_JS})(this, 'input');
|
|
108
|
+
(${FIRE_EVENT_JS})(this, 'change');
|
|
111
109
|
}`;
|
|
112
110
|
/** Object group for remote objects created while selecting files. */
|
|
113
111
|
const UPLOAD_OBJECT_GROUP = 'bdg-upload';
|
|
@@ -176,7 +174,7 @@ async function setFileInput(cdp, selector, value, options) {
|
|
|
176
174
|
return problem;
|
|
177
175
|
try {
|
|
178
176
|
const located = (await cdp.send('Runtime.evaluate', {
|
|
179
|
-
expression: `((el) => (${LABEL_CONTROL_JS})(el) || el)((${FIND_ELEMENTS_JS})(${selectorArgsJS(selector)})[${options.index ?? 0}])`,
|
|
177
|
+
expression: `((el) => (${LABEL_CONTROL_JS})(el) || (${SHADOW_FIELD_JS})(el) || el)((${FIND_ELEMENTS_JS})(${selectorArgsJS(selector)})[${options.index ?? 0}])`,
|
|
180
178
|
objectGroup: UPLOAD_OBJECT_GROUP,
|
|
181
179
|
}));
|
|
182
180
|
if (located.exceptionDetails)
|
|
@@ -441,9 +439,7 @@ export async function clickElement(cdp, selector, options = {}) {
|
|
|
441
439
|
const cdpResponse = response;
|
|
442
440
|
if (cdpResponse.exceptionDetails) {
|
|
443
441
|
throwIfInvalidSelector(cdpResponse.exceptionDetails, selector);
|
|
444
|
-
|
|
445
|
-
const err = clickableElementNotFoundError(selector);
|
|
446
|
-
throw new CommandError(errorMessage, { suggestion: err.suggestion }, EXIT_CODES.SOFTWARE_ERROR);
|
|
442
|
+
throw new ActionScriptError(POINTER_ACTION_NOUN[action], exceptionSummary(cdpResponse.exceptionDetails), selector);
|
|
447
443
|
}
|
|
448
444
|
if (cdpResponse.result?.value && isClickResult(cdpResponse.result.value)) {
|
|
449
445
|
return await performClick(cdp, cdpResponse.result.value, action, options.strict === true);
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { CommandError } from '../../../errors/index.js';
|
|
7
7
|
import { VIA_LABEL_SUFFIX, keyPressFailedError, operationFailedError, unknownKeyError, } from '../../../errors/messages.js';
|
|
8
|
-
import { ELEMENT_IDENTITY_JS } from '../elementInfo.js';
|
|
8
|
+
import { DISABLED_CAUSE_JS, ELEMENT_IDENTITY_JS } from '../elementInfo.js';
|
|
9
9
|
import { throwIfInvalidSelector, withMultipleMatchesWarning, } from './shared.js';
|
|
10
10
|
import { describeModifiers, getKeyDefinition, impliesShift, MODIFIER_FLAGS, parseModifiers, similarKeyNames, shortcutCommands, } from '../keyMapping.js';
|
|
11
11
|
import { FIND_ELEMENTS_JS, LABEL_CONTROL_JS, selectorArgsJS } from '../targetNode.js';
|
|
@@ -60,7 +60,7 @@ const FOCUS_ELEMENT_SCRIPT = `
|
|
|
60
60
|
return {
|
|
61
61
|
success: false,
|
|
62
62
|
reason: 'not-focusable',
|
|
63
|
-
error: 'Element <' + el.tagName.toLowerCase() + '> cannot receive keyboard focus' + (
|
|
63
|
+
error: 'Element <' + el.tagName.toLowerCase() + '> cannot receive keyboard focus' + ((${DISABLED_CAUSE_JS})(el) !== null ? ' (it is disabled)' : '')
|
|
64
64
|
};
|
|
65
65
|
}
|
|
66
66
|
}
|
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Shared internal helpers for form-fill operations: script
|
|
3
|
-
*
|
|
2
|
+
* Shared internal helpers for form-fill operations: script errors and JS
|
|
3
|
+
* string escaping.
|
|
4
4
|
*/
|
|
5
5
|
import type { Protocol } from '../../../connection/typed-cdp.js';
|
|
6
|
+
import { CommandError } from '../../../errors/index.js';
|
|
6
7
|
import type { FillResult } from '../../../ipc/protocol/domTypes.js';
|
|
7
|
-
/**
|
|
8
|
-
* Escape a CSS selector for embedding in a single-quoted JS string.
|
|
9
|
-
*/
|
|
10
|
-
export declare function escapeSelectorForJS(selector: string): string;
|
|
11
8
|
/**
|
|
12
9
|
* Add a warning when the selector matched several elements and no index was
|
|
13
10
|
* given, so the caller knows which one was used. Keeps an existing warning.
|
|
@@ -34,11 +31,20 @@ export declare function withValueMismatchWarning(result: FillResult): FillResult
|
|
|
34
31
|
*/
|
|
35
32
|
export declare function escapeValueForJS(value: string): string;
|
|
36
33
|
/**
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
34
|
+
* An action's page script threw. The action that ran it reports what was
|
|
35
|
+
* thrown; when the page replaced built-ins the script uses,
|
|
36
|
+
* `onScriptTarget` makes that the error instead.
|
|
40
37
|
*/
|
|
41
|
-
export declare
|
|
38
|
+
export declare class ActionScriptError extends CommandError {
|
|
39
|
+
readonly action: string;
|
|
40
|
+
readonly exception: string;
|
|
41
|
+
/**
|
|
42
|
+
* @param action - The action, e.g. `fill`
|
|
43
|
+
* @param exception - What the script threw, in one line
|
|
44
|
+
* @param selector - Selector the action was for
|
|
45
|
+
*/
|
|
46
|
+
constructor(action: string, exception: string, selector: string);
|
|
47
|
+
}
|
|
42
48
|
/**
|
|
43
49
|
* What a page script threw, in one line (`TypeError: Cannot read …` rather
|
|
44
50
|
* than CDP's bare "Uncaught").
|
|
@@ -1,18 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Shared internal helpers for form-fill operations: script
|
|
3
|
-
*
|
|
2
|
+
* Shared internal helpers for form-fill operations: script errors and JS
|
|
3
|
+
* string escaping.
|
|
4
4
|
*/
|
|
5
5
|
import { CommandError } from '../../../errors/index.js';
|
|
6
|
-
import { invalidSelectorError } from '../../../errors/messages.js';
|
|
6
|
+
import { actionScriptFailedError, invalidSelectorError } from '../../../errors/messages.js';
|
|
7
7
|
import { multipleMatchesWarning, valueMismatchWarning } from '../../../ui/messages/commands.js';
|
|
8
8
|
import { EXIT_CODES } from '../../../utils/exitCodes.js';
|
|
9
|
-
import { detectSelectorQuoteDamage } from '../../../utils/shellDetection.js';
|
|
10
|
-
/**
|
|
11
|
-
* Escape a CSS selector for embedding in a single-quoted JS string.
|
|
12
|
-
*/
|
|
13
|
-
export function escapeSelectorForJS(selector) {
|
|
14
|
-
return JSON.stringify(selector).slice(1, -1).replace(/'/g, "\\'");
|
|
15
|
-
}
|
|
16
9
|
/**
|
|
17
10
|
* Add a warning when the selector matched several elements and no index was
|
|
18
11
|
* given, so the caller knows which one was used. Keeps an existing warning.
|
|
@@ -49,50 +42,24 @@ export function escapeValueForJS(value) {
|
|
|
49
42
|
return JSON.stringify(value).slice(1, -1).replace(/'/g, "\\'");
|
|
50
43
|
}
|
|
51
44
|
/**
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
45
|
+
* An action's page script threw. The action that ran it reports what was
|
|
46
|
+
* thrown; when the page replaced built-ins the script uses,
|
|
47
|
+
* `onScriptTarget` makes that the error instead.
|
|
55
48
|
*/
|
|
56
|
-
export
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
lines.push('');
|
|
70
|
-
lines.push('Shell quote damage detected in selector:');
|
|
71
|
-
if (selectorCheck.details) {
|
|
72
|
-
lines.push(` ${selectorCheck.details}`);
|
|
73
|
-
}
|
|
74
|
-
lines.push('');
|
|
75
|
-
lines.push('Try using the two-step pattern:');
|
|
76
|
-
lines.push(` 1. bdg dom query '${selector}'`);
|
|
77
|
-
lines.push(` 2. bdg dom ${operationType} 0${operationType === 'fill' ? ' "value"' : ''}`);
|
|
78
|
-
return lines.join('\n');
|
|
79
|
-
}
|
|
49
|
+
export class ActionScriptError extends CommandError {
|
|
50
|
+
action;
|
|
51
|
+
exception;
|
|
52
|
+
/**
|
|
53
|
+
* @param action - The action, e.g. `fill`
|
|
54
|
+
* @param exception - What the script threw, in one line
|
|
55
|
+
* @param selector - Selector the action was for
|
|
56
|
+
*/
|
|
57
|
+
constructor(action, exception, selector) {
|
|
58
|
+
const err = actionScriptFailedError(action, exception, selector);
|
|
59
|
+
super(err.message, { suggestion: err.suggestion }, EXIT_CODES.SOFTWARE_ERROR);
|
|
60
|
+
this.action = action;
|
|
61
|
+
this.exception = exception;
|
|
80
62
|
}
|
|
81
|
-
const troubleshootingSteps = operationType === 'fill'
|
|
82
|
-
? [
|
|
83
|
-
`1. Verify element exists: bdg dom query "${selector}"`,
|
|
84
|
-
'2. Check element is visible and not disabled',
|
|
85
|
-
`3. Try direct eval: bdg dom eval "document.querySelector('${escapeSelectorForJS(selector)}').value = 'your-value'"`,
|
|
86
|
-
]
|
|
87
|
-
: [
|
|
88
|
-
`1. Verify element exists: bdg dom query "${selector}"`,
|
|
89
|
-
'2. Check element is visible and clickable',
|
|
90
|
-
`3. Try direct eval: bdg dom eval "document.querySelector('${escapeSelectorForJS(selector)}').click()"`,
|
|
91
|
-
];
|
|
92
|
-
lines.push('');
|
|
93
|
-
lines.push('Troubleshooting:');
|
|
94
|
-
lines.push(` ${troubleshootingSteps.join('\n ')}`);
|
|
95
|
-
return lines.join('\n');
|
|
96
63
|
}
|
|
97
64
|
/**
|
|
98
65
|
* What a page script threw, in one line (`TypeError: Cannot read …` rather
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import { CDPTimeoutError } from '../../connection/errors.js';
|
|
5
5
|
import { trackInFlightRequests } from '../../connection/inFlightRequests.js';
|
|
6
6
|
import { submitNetworkBusyWarning, submitTimeoutError } from '../../errors/messages.js';
|
|
7
|
-
import { ELEMENT_IDENTITY_JS } from './elementInfo.js';
|
|
7
|
+
import { DISABLED_CAUSE_JS, ELEMENT_IDENTITY_JS } from './elementInfo.js';
|
|
8
8
|
import { throwIfInvalidSelector } from './formFillHelpers/shared.js';
|
|
9
9
|
import { FIND_ELEMENTS_JS, selectorArgsJS } from './targetNode.js';
|
|
10
10
|
import { summarizePendingRequests } from '../page/loadingState.js';
|
|
@@ -49,7 +49,8 @@ const PREPARE_SUBMIT_SCRIPT = `
|
|
|
49
49
|
f.matches('button:not([type]), button[type=submit], input[type=submit], input[type=image]')
|
|
50
50
|
)
|
|
51
51
|
: [];
|
|
52
|
-
|
|
52
|
+
const disabled = (f) => (${DISABLED_CAUSE_JS})(f) !== null;
|
|
53
|
+
if (submitters.length > 0 && submitters.every(disabled)) {
|
|
53
54
|
// A user could not submit: Enter does nothing while the default button is disabled
|
|
54
55
|
return { action: 'fail', reason: 'disabled', error: 'The form\\'s submit button is disabled' };
|
|
55
56
|
}
|
|
@@ -62,7 +63,7 @@ const PREPARE_SUBMIT_SCRIPT = `
|
|
|
62
63
|
if (isForm) {
|
|
63
64
|
// Like pressing Enter: the form's default button is the submitter, so its
|
|
64
65
|
// name=value is sent too
|
|
65
|
-
const submitter = submitters.find((f) => !f
|
|
66
|
+
const submitter = submitters.find((f) => !disabled(f));
|
|
66
67
|
const element = (${ELEMENT_IDENTITY_JS})(submitter || el);
|
|
67
68
|
if (submitter) el.requestSubmit(submitter);
|
|
68
69
|
else el.requestSubmit();
|
|
@@ -127,6 +127,7 @@ export function placeInOwnerFrame(raw, owner, mapping) {
|
|
|
127
127
|
clipper: inner.clipper ?? (inside(rect, frameView) ? outer.clipper : frame.element),
|
|
128
128
|
hidden: inner.hidden ?? (outer.hidden ? 'inside a hidden iframe' : null),
|
|
129
129
|
invisible: inner.invisible ?? outer.invisible,
|
|
130
|
+
masked: inner.invisible ? null : (inner.masked ?? outer.masked ?? null),
|
|
130
131
|
fixed: outer.fixed,
|
|
131
132
|
sticky: outer.sticky ?? false,
|
|
132
133
|
pageScroll: outer.pageScroll,
|
|
@@ -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,16 +13,18 @@
|
|
|
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
|
+
import { explainUnsetVariables } from './inspectHints.js';
|
|
20
21
|
import { buildInspectResult } from './inspectModel.js';
|
|
21
22
|
import { matchedStyles, sourceLabel, trackStyleSheets } from './inspectRules.js';
|
|
22
|
-
import { INSPECT_PAGE_JS, RELATED_NODE_JS } from './inspectScripts.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';
|
|
27
|
+
import { evaluateInBdgWorld, resolveNodeInBdgWorld } from '../page/bdgWorld.js';
|
|
26
28
|
import { createLogger } from '../../ui/logging/index.js';
|
|
27
29
|
import { getErrorMessage } from '../../utils/errors.js';
|
|
28
30
|
import { EXIT_CODES } from '../../utils/exitCodes.js';
|
|
@@ -36,12 +38,18 @@ const HINTS_BUDGET_MS = 1000;
|
|
|
36
38
|
const RULES_BUDGET_MS = 5000;
|
|
37
39
|
/** Distinguishes the object groups of concurrent calls */
|
|
38
40
|
let groupCounter = 0;
|
|
39
|
-
/**
|
|
41
|
+
/**
|
|
42
|
+
* Page-side choice of a match: the index asked for, else the first visible
|
|
43
|
+
* one (rendered, not `visibility: hidden`, not `opacity: 0`), else the first
|
|
44
|
+
* rendered one, else the first
|
|
45
|
+
*/
|
|
40
46
|
const PICK_MATCH_JS = `function (i) {
|
|
41
47
|
if (i !== null) return i;
|
|
42
|
-
const
|
|
43
|
-
const
|
|
44
|
-
|
|
48
|
+
const check = (el, options) => (el.checkVisibility ? el.checkVisibility(options) : el.getClientRects().length > 0);
|
|
49
|
+
const visible = Array.prototype.findIndex.call(this, (el) => check(el, { visibilityProperty: true, opacityProperty: true }));
|
|
50
|
+
if (visible >= 0) return visible;
|
|
51
|
+
const rendered = Array.prototype.findIndex.call(this, (el) => check(el, {}));
|
|
52
|
+
return rendered < 0 ? 0 : rendered;
|
|
45
53
|
}`;
|
|
46
54
|
/**
|
|
47
55
|
* Inspect one element.
|
|
@@ -54,10 +62,20 @@ const PICK_MATCH_JS = `function (i) {
|
|
|
54
62
|
*/
|
|
55
63
|
export async function inspectElement(cdp, params) {
|
|
56
64
|
const started = Date.now();
|
|
65
|
+
if (params.why === 'all') {
|
|
66
|
+
const err = whyAllPropertyError();
|
|
67
|
+
throw new CommandError(err.message, { suggestion: err.suggestion }, EXIT_CODES.INVALID_ARGUMENTS);
|
|
68
|
+
}
|
|
69
|
+
const pseudoOf = /::?(before|after)\s*$/i.exec(params.selector)?.[1]?.toLowerCase();
|
|
70
|
+
const request = pseudoOf
|
|
71
|
+
? { ...params, selector: params.selector.replace(/::?(before|after)\s*$/i, '') }
|
|
72
|
+
: params;
|
|
57
73
|
const objectGroup = `bdg-inspect-${++groupCounter}`;
|
|
58
74
|
try {
|
|
59
|
-
const found = await findElement(cdp,
|
|
60
|
-
const sources = await readSources(cdp, found.objectId,
|
|
75
|
+
const found = await findElement(cdp, request, objectGroup);
|
|
76
|
+
const sources = await readSources(cdp, found.objectId, request, objectGroup);
|
|
77
|
+
if (sources.raw.unknownWhy)
|
|
78
|
+
throwUnknownProperties([params.why ?? ''], sources);
|
|
61
79
|
const propValues = sources.props ? checkedProps(sources.props, sources) : undefined;
|
|
62
80
|
const built = buildInspectResult(sources, {
|
|
63
81
|
selector: params.selector,
|
|
@@ -67,8 +85,14 @@ export async function inspectElement(cdp, params) {
|
|
|
67
85
|
...(params.all && { all: true }),
|
|
68
86
|
...(propValues && { propValues }),
|
|
69
87
|
});
|
|
70
|
-
const
|
|
71
|
-
const
|
|
88
|
+
const cascade = cascadeFields(cdp, sources);
|
|
89
|
+
const hints = cascade.hints && (await explainedHints(cdp, found.objectId, cascade.hints));
|
|
90
|
+
const withCascade = { ...built, ...cascade, ...(hints && { hints }) };
|
|
91
|
+
const result = {
|
|
92
|
+
...withCascade,
|
|
93
|
+
...(found.picked && { picked: found.picked }),
|
|
94
|
+
...(pseudoOf && { pseudoOf: `::${pseudoOf}` }),
|
|
95
|
+
};
|
|
72
96
|
log.debug(`Inspected ${result.element} in ${Date.now() - started} ms`);
|
|
73
97
|
return result;
|
|
74
98
|
}
|
|
@@ -78,6 +102,25 @@ export async function inspectElement(cdp, params) {
|
|
|
78
102
|
.catch((error) => log.debug(`Object group not released: ${getErrorMessage(error)}`));
|
|
79
103
|
}
|
|
80
104
|
}
|
|
105
|
+
/**
|
|
106
|
+
* Hints with unset variables told where the page sets them
|
|
107
|
+
* ({@link VARIABLE_SETTERS_JS}); unchanged when there are none or the
|
|
108
|
+
* search fails.
|
|
109
|
+
*
|
|
110
|
+
* @param cdp - CDP connection
|
|
111
|
+
* @param objectId - The element
|
|
112
|
+
* @param hints - Hints
|
|
113
|
+
* @returns Hints
|
|
114
|
+
*/
|
|
115
|
+
async function explainedHints(cdp, objectId, hints) {
|
|
116
|
+
const names = [...new Set(hints.flatMap((hint) => hint.variables ?? []))];
|
|
117
|
+
if (names.length === 0)
|
|
118
|
+
return hints;
|
|
119
|
+
const setters = await callOn(cdp, objectId, VARIABLE_SETTERS_JS, [
|
|
120
|
+
names,
|
|
121
|
+
]);
|
|
122
|
+
return setters ? explainUnsetVariables(hints, setters) : hints;
|
|
123
|
+
}
|
|
81
124
|
/**
|
|
82
125
|
* The properties asked for with `--props`.
|
|
83
126
|
*
|
|
@@ -87,9 +130,19 @@ export async function inspectElement(cdp, params) {
|
|
|
87
130
|
* @throws CommandError (81) for a name no value was found for
|
|
88
131
|
*/
|
|
89
132
|
function checkedProps(names, sources) {
|
|
90
|
-
const { props, unknown } = selectedProps(names, sources.style, sources.raw.props, sources.raw.unknownProps);
|
|
133
|
+
const { props, unknown } = selectedProps(names, sources.style, sources.raw.props, sources.raw.unknownProps, sources.raw.rootFontSize);
|
|
91
134
|
if (unknown.length === 0)
|
|
92
135
|
return props;
|
|
136
|
+
return throwUnknownProperties(unknown, sources);
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Reject names that are not CSS properties, with the closest computed ones.
|
|
140
|
+
*
|
|
141
|
+
* @param unknown - Names
|
|
142
|
+
* @param sources - What was read (computed property names)
|
|
143
|
+
* @throws CommandError (81) always
|
|
144
|
+
*/
|
|
145
|
+
function throwUnknownProperties(unknown, sources) {
|
|
93
146
|
const suggestions = unknown.flatMap((name) => findSimilar(name, Object.keys(sources.style), { maxSuggestions: 1 }));
|
|
94
147
|
const err = unknownCssPropertyError(unknown, suggestions);
|
|
95
148
|
throw new CommandError(err.message, { suggestion: err.suggestion }, EXIT_CODES.INVALID_ARGUMENTS);
|
|
@@ -130,11 +183,14 @@ function cascadeFields(cdp, sources) {
|
|
|
130
183
|
style: sources.style,
|
|
131
184
|
parentStyle: sources.parentStyle,
|
|
132
185
|
replaced: sources.raw.replaced === true,
|
|
133
|
-
formControl: sources.raw.formControl,
|
|
186
|
+
formControl: sources.raw.formControl && sources.raw.hasText,
|
|
134
187
|
...(sources.hints === false && { hints: false }),
|
|
135
188
|
label: (declaration) => sourceLabel(declaration, cdp),
|
|
136
189
|
...(sources.rules && { rules: true }),
|
|
137
190
|
...(sources.why && { why: sources.why }),
|
|
191
|
+
...(sources.raw.whyLonghands && { whyLonghands: sources.raw.whyLonghands }),
|
|
192
|
+
...(sources.raw.whyComputed && { whyComputed: sources.raw.whyComputed }),
|
|
193
|
+
...(sources.raw.whyInactive && { whyInactive: sources.raw.whyInactive }),
|
|
138
194
|
...(sources.props && { props: sources.props }),
|
|
139
195
|
});
|
|
140
196
|
}
|
|
@@ -193,9 +249,7 @@ function pickedHow(params, count, index) {
|
|
|
193
249
|
* @returns Remote object id, or undefined when the node left the page
|
|
194
250
|
*/
|
|
195
251
|
async function resolveCachedNode(cdp, backendNodeId, objectGroup) {
|
|
196
|
-
const resolved = (await cdp
|
|
197
|
-
.send('DOM.resolveNode', { backendNodeId, objectGroup })
|
|
198
|
-
.catch((error) => {
|
|
252
|
+
const resolved = (await resolveNodeInBdgWorld(cdp, { backendNodeId, objectGroup }).catch((error) => {
|
|
199
253
|
log.debug(`Node ${backendNodeId} not resolved: ${getErrorMessage(error)}`);
|
|
200
254
|
return {};
|
|
201
255
|
}));
|
|
@@ -215,10 +269,10 @@ async function resolveCachedNode(cdp, backendNodeId, objectGroup) {
|
|
|
215
269
|
* @throws CommandError (81) invalid selector, (91) page script failure
|
|
216
270
|
*/
|
|
217
271
|
async function querySelector(cdp, selector, objectGroup) {
|
|
218
|
-
const response =
|
|
272
|
+
const response = await evaluateInBdgWorld(cdp, {
|
|
219
273
|
expression: `(${DEEP_QUERY_JS})(${selectorArgsJS(selector)})`,
|
|
220
274
|
objectGroup,
|
|
221
|
-
})
|
|
275
|
+
});
|
|
222
276
|
if (response.exceptionDetails || !response.result.objectId) {
|
|
223
277
|
if (response.exceptionDetails)
|
|
224
278
|
throwIfInvalidSelector(response.exceptionDetails, selector);
|
|
@@ -269,7 +323,7 @@ async function readSources(cdp, objectId, params, objectGroup) {
|
|
|
269
323
|
return {
|
|
270
324
|
raw,
|
|
271
325
|
...styles,
|
|
272
|
-
fonts: raw.
|
|
326
|
+
fonts: raw.textHolder ? styles.fonts.textHolder : styles.fonts.node,
|
|
273
327
|
...measured,
|
|
274
328
|
...(params.rules && { rules: true }),
|
|
275
329
|
...(params.why && { why: params.why }),
|
|
@@ -290,6 +344,7 @@ async function readPage(cdp, objectId, params) {
|
|
|
290
344
|
const raw = await callOn(cdp, objectId, INSPECT_PAGE_JS, [
|
|
291
345
|
params.props || params.all ? 0 : (params.tree ?? DEFAULT_TREE_DEPTH),
|
|
292
346
|
params.props ?? null,
|
|
347
|
+
params.why ?? null,
|
|
293
348
|
]);
|
|
294
349
|
if (raw)
|
|
295
350
|
return raw;
|
|
@@ -297,11 +352,12 @@ async function readPage(cdp, objectId, params) {
|
|
|
297
352
|
throw new CommandError(err.message, { suggestion: err.suggestion }, EXIT_CODES.SCRIPT_ERROR);
|
|
298
353
|
}
|
|
299
354
|
/**
|
|
300
|
-
* The backend node id of the element's layout parent
|
|
355
|
+
* The backend node id of the element's layout parent, the element that
|
|
356
|
+
* draws most of its text, or the parent of that text's nodes.
|
|
301
357
|
*
|
|
302
358
|
* @param cdp - CDP connection
|
|
303
359
|
* @param objectId - The element
|
|
304
|
-
* @param which - `parent` or `
|
|
360
|
+
* @param which - `parent`, `textHolder` or `fontHolder`
|
|
305
361
|
* @param objectGroup - Object group for handles
|
|
306
362
|
* @returns Backend node id, or undefined when there is none
|
|
307
363
|
*/
|
|
@@ -316,8 +372,8 @@ async function relatedNode(cdp, objectId, which, objectGroup) {
|
|
|
316
372
|
return id ? (await describe(cdp, id)).backendNodeId : undefined;
|
|
317
373
|
}
|
|
318
374
|
/**
|
|
319
|
-
* The backend node ids of the element, its layout parent, its
|
|
320
|
-
*
|
|
375
|
+
* The backend node ids of the element, its layout parent, its text holder
|
|
376
|
+
* (with the parent of its text nodes) and its generated pseudo-elements.
|
|
321
377
|
*
|
|
322
378
|
* @param cdp - CDP connection
|
|
323
379
|
* @param objectId - The element
|
|
@@ -325,10 +381,11 @@ async function relatedNode(cdp, objectId, which, objectGroup) {
|
|
|
325
381
|
* @returns Backend node ids
|
|
326
382
|
*/
|
|
327
383
|
async function relatedNodes(cdp, objectId, objectGroup) {
|
|
328
|
-
const [node, parent, textHolder] = await Promise.all([
|
|
384
|
+
const [node, parent, textHolder, fontHolder] = await Promise.all([
|
|
329
385
|
describe(cdp, objectId),
|
|
330
386
|
relatedNode(cdp, objectId, 'parent', objectGroup),
|
|
331
387
|
relatedNode(cdp, objectId, 'textHolder', objectGroup),
|
|
388
|
+
relatedNode(cdp, objectId, 'fontHolder', objectGroup),
|
|
332
389
|
]);
|
|
333
390
|
const pseudo = (node.pseudoElements ?? [])
|
|
334
391
|
.filter((p) => p.pseudoType === 'before' || p.pseudoType === 'after')
|
|
@@ -339,7 +396,8 @@ async function relatedNodes(cdp, objectId, objectGroup) {
|
|
|
339
396
|
return {
|
|
340
397
|
node: node.backendNodeId,
|
|
341
398
|
...(parent && { parent }),
|
|
342
|
-
...(textHolder && { textHolder }),
|
|
399
|
+
...(textHolder && textHolder !== node.backendNodeId && { textHolder }),
|
|
400
|
+
...(fontHolder && { fontHolder }),
|
|
343
401
|
pseudo,
|
|
344
402
|
};
|
|
345
403
|
}
|
|
@@ -392,6 +450,7 @@ async function nodeIdLookup(cdp, related) {
|
|
|
392
450
|
related.node,
|
|
393
451
|
related.parent,
|
|
394
452
|
related.textHolder,
|
|
453
|
+
related.fontHolder,
|
|
395
454
|
...related.pseudo.map((p) => p.backendNodeId),
|
|
396
455
|
];
|
|
397
456
|
const ids = await pushNodes(cdp, order.filter((id) => id !== undefined));
|
|
@@ -408,18 +467,23 @@ async function nodeIdLookup(cdp, related) {
|
|
|
408
467
|
async function readStyles(cdp, related, params) {
|
|
409
468
|
await enableStyleDomains(cdp);
|
|
410
469
|
const nodeIdOf = await nodeIdLookup(cdp, related);
|
|
411
|
-
const
|
|
470
|
+
const optionalStyle = (backendNodeId) => backendNodeId === undefined
|
|
471
|
+
? Promise.resolve(undefined)
|
|
472
|
+
: computedStyle(cdp, nodeIdOf(backendNodeId));
|
|
473
|
+
const [matched, style, parentStyle, holderStyle, nodeFonts, holderFonts, size, pseudo] = await Promise.all([
|
|
412
474
|
readMatched(cdp, nodeIdOf(related.node), params),
|
|
413
475
|
computedStyle(cdp, nodeIdOf(related.node)),
|
|
414
|
-
|
|
476
|
+
optionalStyle(related.parent),
|
|
477
|
+
optionalStyle(related.textHolder),
|
|
415
478
|
platformFonts(cdp, nodeIdOf(related.node)),
|
|
416
|
-
platformFonts(cdp, nodeIdOf(related.
|
|
479
|
+
platformFonts(cdp, nodeIdOf(related.fontHolder)),
|
|
417
480
|
borderBoxSize(cdp, related.node),
|
|
418
481
|
Promise.all(related.pseudo.map((p) => pseudoSource(cdp, p.type, p.backendNodeId, nodeIdOf(p.backendNodeId)))),
|
|
419
482
|
]);
|
|
420
483
|
return {
|
|
421
484
|
style,
|
|
422
485
|
...(parentStyle && { parentStyle }),
|
|
486
|
+
...(holderStyle && { holderStyle }),
|
|
423
487
|
pseudo,
|
|
424
488
|
fonts: { node: nodeFonts, textHolder: holderFonts },
|
|
425
489
|
...(size && { size }),
|
|
@@ -466,7 +530,7 @@ async function pseudoSource(cdp, type, backendNodeId, nodeId) {
|
|
|
466
530
|
*
|
|
467
531
|
* @param cdp - CDP connection
|
|
468
532
|
*/
|
|
469
|
-
async function enableStyleDomains(cdp) {
|
|
533
|
+
export async function enableStyleDomains(cdp) {
|
|
470
534
|
if (stylesEnabled.has(cdp))
|
|
471
535
|
return;
|
|
472
536
|
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
|
};
|