browser-debugger-cli 0.8.0 → 0.10.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 +7 -1
- package/dist/cdp/schema.d.ts +4 -1
- package/dist/cdp/schema.js +48 -7
- package/dist/commands/cdp.js +3 -2
- package/dist/commands/cleanup.d.ts +11 -0
- package/dist/commands/cleanup.js +161 -57
- package/dist/commands/console.d.ts +20 -1
- package/dist/commands/console.js +57 -17
- package/dist/commands/details.js +3 -2
- package/dist/commands/dom/DomElementResolver.d.ts +10 -3
- package/dist/commands/dom/DomElementResolver.js +35 -17
- package/dist/commands/dom/a11y.d.ts +10 -0
- package/dist/commands/dom/a11y.js +29 -6
- package/dist/commands/dom/eval.d.ts +3 -1
- package/dist/commands/dom/eval.js +29 -4
- package/dist/commands/dom/form.js +16 -62
- package/dist/commands/dom/formInteraction.js +189 -119
- package/dist/commands/dom/formSummary.d.ts +49 -0
- package/dist/commands/dom/formSummary.js +180 -0
- package/dist/commands/dom/frames.d.ts +2 -1
- package/dist/commands/dom/frames.js +17 -2
- package/dist/commands/dom/get.d.ts +6 -5
- package/dist/commands/dom/get.js +92 -82
- 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 +20 -0
- package/dist/commands/dom/helpers/keyAttributes.js +54 -0
- package/dist/commands/dom/helpers/query.d.ts +44 -17
- package/dist/commands/dom/helpers/query.js +300 -106
- package/dist/commands/dom/helpers/runElementCommand.d.ts +10 -2
- package/dist/commands/dom/helpers/runElementCommand.js +98 -30
- package/dist/commands/dom/helpers/screenshot.d.ts +4 -1
- package/dist/commands/dom/helpers/screenshot.js +239 -51
- package/dist/commands/dom/index.d.ts +4 -1
- package/dist/commands/dom/index.js +22 -7
- package/dist/commands/dom/inspect.d.ts +15 -0
- package/dist/commands/dom/inspect.js +82 -0
- package/dist/commands/dom/layout.d.ts +14 -0
- package/dist/commands/dom/layout.js +54 -0
- package/dist/commands/dom/listeners.d.ts +5 -1
- package/dist/commands/dom/listeners.js +15 -5
- package/dist/commands/dom/query.js +2 -3
- package/dist/commands/dom/screenshot.d.ts +12 -2
- package/dist/commands/dom/screenshot.js +27 -3
- package/dist/commands/dom/semanticUtils.d.ts +16 -10
- package/dist/commands/dom/semanticUtils.js +53 -16
- package/dist/commands/dom/wait.d.ts +13 -0
- package/dist/commands/dom/wait.js +83 -0
- package/dist/commands/helpJson.js +2 -2
- package/dist/commands/network/list.js +17 -13
- package/dist/commands/optionBehaviors.js +154 -21
- package/dist/commands/page.d.ts +3 -2
- package/dist/commands/page.js +100 -5
- package/dist/commands/peek.js +4 -11
- package/dist/commands/sessions.d.ts +8 -0
- package/dist/commands/sessions.js +19 -0
- package/dist/commands/shared/CommandRunner.js +4 -4
- package/dist/commands/shared/commonOptions.d.ts +4 -0
- package/dist/commands/shared/commonOptions.js +9 -0
- package/dist/commands/shared/dataFetcher.js +2 -2
- package/dist/commands/shared/followMode.d.ts +21 -1
- package/dist/commands/shared/followMode.js +29 -2
- package/dist/commands/shared/handleValidationError.d.ts +2 -2
- package/dist/commands/shared/handleValidationError.js +12 -3
- package/dist/commands/shared/optionTypes.d.ts +61 -5
- package/dist/commands/shared/startHelpers.d.ts +66 -0
- package/dist/commands/shared/startHelpers.js +103 -13
- package/dist/commands/shared/validation.d.ts +14 -2
- package/dist/commands/shared/validation.js +20 -3
- package/dist/commands/start.d.ts +63 -0
- package/dist/commands/start.js +115 -15
- package/dist/commands/status.js +29 -7
- package/dist/commands/stop.js +7 -6
- package/dist/commands/tail.js +4 -11
- package/dist/commands/types.d.ts +2 -0
- package/dist/commands.js +2 -0
- package/dist/connection/chromeIdentity.d.ts +65 -0
- package/dist/connection/chromeIdentity.js +143 -0
- package/dist/connection/launcher/profilePreferences.d.ts +47 -0
- package/dist/connection/launcher/profilePreferences.js +151 -0
- package/dist/connection/launcher.d.ts +21 -2
- package/dist/connection/launcher.js +42 -16
- package/dist/connection/portReservation.d.ts +14 -4
- package/dist/connection/portReservation.js +21 -6
- package/dist/connection/startupExit.d.ts +8 -0
- package/dist/connection/startupExit.js +15 -6
- package/dist/constants.d.ts +6 -2
- package/dist/constants.js +9 -2
- package/dist/daemon/SessionController.js +23 -7
- package/dist/daemon/errors.d.ts +1 -1
- package/dist/daemon/errors.js +1 -1
- package/dist/daemon/launcher.d.ts +10 -2
- package/dist/daemon/launcher.js +8 -7
- package/dist/daemon/server/SocketServer.js +1 -2
- package/dist/daemon/session/Session.d.ts +20 -0
- package/dist/daemon/session/Session.js +80 -9
- package/dist/daemon/session/chromeConnection.d.ts +9 -0
- package/dist/daemon/session/chromeConnection.js +45 -8
- package/dist/daemon/session/commandRegistry.d.ts +14 -1
- package/dist/daemon/session/commandRegistry.js +113 -67
- package/dist/daemon/session/interactions.d.ts +48 -9
- package/dist/daemon/session/interactions.js +46 -9
- package/dist/daemon/session/triggeredRequests.d.ts +67 -0
- package/dist/daemon/session/triggeredRequests.js +157 -0
- package/dist/daemon/session/types.d.ts +5 -1
- package/dist/daemon.js +10630 -3601
- package/dist/errors/messages.d.ts +456 -24
- package/dist/errors/messages.js +862 -67
- package/dist/index.js +6915 -3401
- package/dist/ipc/client.d.ts +21 -1
- package/dist/ipc/client.js +35 -3
- package/dist/ipc/protocol/commands.d.ts +145 -5
- package/dist/ipc/protocol/commands.js +4 -0
- package/dist/ipc/protocol/domTypes.d.ts +291 -7
- package/dist/ipc/protocol/inspectTypes.d.ts +388 -0
- package/dist/ipc/protocol/inspectTypes.js +10 -0
- package/dist/ipc/session/lifecycle.d.ts +8 -1
- package/dist/ipc/session/queries.d.ts +5 -1
- package/dist/ipc/session/types.d.ts +5 -0
- package/dist/ipc/transport/index.d.ts +2 -1
- package/dist/ipc/transport/index.js +2 -2
- package/dist/runtime/dom/actionEffects.d.ts +185 -0
- package/dist/runtime/dom/actionEffects.js +402 -0
- package/dist/runtime/dom/actionEffectsScripts.d.ts +90 -0
- package/dist/runtime/dom/actionEffectsScripts.js +426 -0
- package/dist/runtime/dom/elementGeometry.d.ts +170 -0
- package/dist/runtime/dom/elementGeometry.js +553 -0
- package/dist/runtime/dom/elementInfo.d.ts +103 -0
- package/dist/runtime/dom/elementInfo.js +256 -0
- package/dist/runtime/dom/evalHelpers.d.ts +51 -6
- package/dist/runtime/dom/evalHelpers.js +136 -26
- package/dist/runtime/dom/eventListeners.d.ts +2 -1
- package/dist/runtime/dom/eventListeners.js +184 -47
- package/dist/runtime/dom/formDiscovery.d.ts +1 -1
- package/dist/runtime/dom/formDiscovery.js +116 -16
- package/dist/runtime/dom/formFillHelpers/fill.d.ts +9 -0
- package/dist/runtime/dom/formFillHelpers/fill.js +178 -14
- package/dist/runtime/dom/formFillHelpers/index.d.ts +2 -2
- package/dist/runtime/dom/formFillHelpers/index.js +2 -2
- package/dist/runtime/dom/formFillHelpers/pressKey.js +14 -3
- package/dist/runtime/dom/formFillHelpers/scroll.d.ts +3 -0
- package/dist/runtime/dom/formFillHelpers/scroll.js +60 -18
- package/dist/runtime/dom/formFillHelpers/shared.d.ts +17 -0
- package/dist/runtime/dom/formFillHelpers/shared.js +25 -1
- package/dist/runtime/dom/formFillHelpers/stability.d.ts +20 -6
- package/dist/runtime/dom/formFillHelpers/stability.js +50 -19
- package/dist/runtime/dom/formSubmitHelpers.d.ts +3 -0
- package/dist/runtime/dom/formSubmitHelpers.js +89 -15
- package/dist/runtime/dom/frameLayout.d.ts +60 -0
- package/dist/runtime/dom/frameLayout.js +140 -0
- package/dist/runtime/dom/frameOrigin.d.ts +50 -0
- package/dist/runtime/dom/frameOrigin.js +62 -0
- package/dist/runtime/dom/frameScopedConnection.d.ts +92 -0
- package/dist/runtime/dom/frameScopedConnection.js +252 -0
- package/dist/runtime/dom/frameSelection.d.ts +12 -1
- package/dist/runtime/dom/frameSelection.js +22 -3
- package/dist/runtime/dom/frames.d.ts +61 -5
- package/dist/runtime/dom/frames.js +329 -75
- package/dist/runtime/dom/inspect.d.ts +28 -0
- package/dist/runtime/dom/inspect.js +557 -0
- package/dist/runtime/dom/inspectAllStyles.d.ts +62 -0
- package/dist/runtime/dom/inspectAllStyles.js +385 -0
- package/dist/runtime/dom/inspectCascade.d.ts +94 -0
- package/dist/runtime/dom/inspectCascade.js +371 -0
- package/dist/runtime/dom/inspectCascadeModel.d.ts +39 -0
- package/dist/runtime/dom/inspectCascadeModel.js +232 -0
- package/dist/runtime/dom/inspectHints.d.ts +62 -0
- package/dist/runtime/dom/inspectHints.js +305 -0
- package/dist/runtime/dom/inspectLayoutModel.d.ts +87 -0
- package/dist/runtime/dom/inspectLayoutModel.js +346 -0
- package/dist/runtime/dom/inspectModel.d.ts +74 -0
- package/dist/runtime/dom/inspectModel.js +184 -0
- package/dist/runtime/dom/inspectPaintModel.d.ts +157 -0
- package/dist/runtime/dom/inspectPaintModel.js +461 -0
- package/dist/runtime/dom/inspectRules.d.ts +37 -0
- package/dist/runtime/dom/inspectRules.js +101 -0
- package/dist/runtime/dom/inspectScripts.d.ts +132 -0
- package/dist/runtime/dom/inspectScripts.js +263 -0
- package/dist/runtime/dom/inspectTree.d.ts +40 -0
- package/dist/runtime/dom/inspectTree.js +134 -0
- package/dist/runtime/dom/inspectVariables.d.ts +33 -0
- package/dist/runtime/dom/inspectVariables.js +94 -0
- package/dist/runtime/dom/inspectWhyModel.d.ts +20 -0
- package/dist/runtime/dom/inspectWhyModel.js +134 -0
- package/dist/runtime/dom/layout.d.ts +71 -0
- package/dist/runtime/dom/layout.js +340 -0
- package/dist/runtime/dom/listenerPageScripts.d.ts +72 -0
- package/dist/runtime/dom/listenerPageScripts.js +365 -0
- package/dist/runtime/dom/listenerSummary.d.ts +136 -11
- package/dist/runtime/dom/listenerSummary.js +361 -22
- package/dist/runtime/dom/pageActivity.d.ts +41 -0
- package/dist/runtime/dom/pageActivity.js +123 -0
- package/dist/runtime/dom/reactEventHelpers.d.ts +63 -2
- package/dist/runtime/dom/reactEventHelpers.js +220 -41
- package/dist/runtime/dom/targetNode.d.ts +80 -27
- package/dist/runtime/dom/targetNode.js +249 -33
- package/dist/runtime/dom/wait.d.ts +25 -0
- package/dist/runtime/dom/wait.js +199 -0
- package/dist/runtime/dom/waitCondition.d.ts +71 -0
- package/dist/runtime/dom/waitCondition.js +75 -0
- package/dist/runtime/page/emulation.d.ts +71 -0
- package/dist/runtime/page/emulation.js +117 -0
- package/dist/runtime/page/loadingState.d.ts +36 -0
- package/dist/runtime/page/loadingState.js +86 -0
- package/dist/runtime/page/navigation.d.ts +46 -2
- package/dist/runtime/page/navigation.js +69 -33
- package/dist/session/QueryCacheManager.d.ts +11 -1
- package/dist/session/QueryCacheManager.js +25 -3
- package/dist/session/chromeOwners.d.ts +34 -0
- package/dist/session/chromeOwners.js +51 -0
- package/dist/session/cleanup/staleSession.d.ts +11 -1
- package/dist/session/cleanup/staleSession.js +17 -6
- package/dist/session/cleanup/userCommands.js +2 -4
- package/dist/session/metadata.d.ts +5 -1
- package/dist/session/metadata.js +2 -1
- package/dist/session/paths.d.ts +77 -3
- package/dist/session/paths.js +111 -5
- package/dist/session/port.d.ts +31 -7
- package/dist/session/port.js +50 -43
- package/dist/session/portClaims.d.ts +66 -0
- package/dist/session/portClaims.js +284 -0
- package/dist/session/sessionList.d.ts +58 -0
- package/dist/session/sessionList.js +199 -0
- package/dist/session/sessionName.d.ts +46 -0
- package/dist/session/sessionName.js +97 -0
- package/dist/telemetry/a11y.d.ts +18 -3
- package/dist/telemetry/a11y.js +170 -29
- package/dist/telemetry/console.d.ts +1 -0
- package/dist/telemetry/console.js +100 -5
- package/dist/telemetry/network.js +3 -1
- package/dist/telemetry/requestKinds.d.ts +32 -0
- package/dist/telemetry/requestKinds.js +61 -0
- package/dist/telemetry/requestState.d.ts +31 -0
- package/dist/telemetry/requestState.js +38 -0
- package/dist/types.d.ts +112 -3
- package/dist/ui/formatters/a11y.js +3 -0
- package/dist/ui/formatters/console/chronological.d.ts +8 -0
- package/dist/ui/formatters/console/chronological.js +17 -4
- package/dist/ui/formatters/console/json.js +3 -4
- package/dist/ui/formatters/console/shared.d.ts +12 -0
- package/dist/ui/formatters/console.d.ts +2 -2
- package/dist/ui/formatters/console.js +1 -1
- package/dist/ui/formatters/details.d.ts +8 -0
- package/dist/ui/formatters/details.js +61 -4
- package/dist/ui/formatters/dom.d.ts +27 -14
- package/dist/ui/formatters/dom.js +88 -59
- package/dist/ui/formatters/form.js +29 -18
- package/dist/ui/formatters/inspect.d.ts +39 -0
- package/dist/ui/formatters/inspect.js +596 -0
- package/dist/ui/formatters/keyAttributes.d.ts +19 -0
- package/dist/ui/formatters/keyAttributes.js +84 -0
- package/dist/ui/formatters/layout.d.ts +31 -0
- package/dist/ui/formatters/layout.js +53 -0
- package/dist/ui/formatters/listeners.d.ts +3 -2
- package/dist/ui/formatters/listeners.js +73 -9
- package/dist/ui/formatters/networkHeaders.d.ts +13 -0
- package/dist/ui/formatters/networkHeaders.js +36 -3
- package/dist/ui/formatters/networkList.d.ts +29 -1
- package/dist/ui/formatters/networkList.js +86 -20
- package/dist/ui/formatters/preview.js +2 -1
- package/dist/ui/formatters/requestStatus.d.ts +1 -17
- package/dist/ui/formatters/requestStatus.js +2 -30
- package/dist/ui/formatters/sessions.d.ts +12 -0
- package/dist/ui/formatters/sessions.js +40 -0
- package/dist/ui/formatters/status.d.ts +21 -2
- package/dist/ui/formatters/status.js +47 -10
- package/dist/ui/formatters/triggeredRequests.d.ts +36 -0
- package/dist/ui/formatters/triggeredRequests.js +65 -0
- package/dist/ui/formatting.d.ts +19 -0
- package/dist/ui/formatting.js +31 -36
- package/dist/ui/messages/chrome.d.ts +9 -0
- package/dist/ui/messages/chrome.js +17 -5
- package/dist/ui/messages/commands.d.ts +504 -14
- package/dist/ui/messages/commands.js +835 -21
- package/dist/ui/messages/consoleMessages.d.ts +10 -0
- package/dist/ui/messages/consoleMessages.js +17 -0
- package/dist/ui/messages/hints.js +2 -1
- package/dist/ui/messages/networkMessages.d.ts +14 -0
- package/dist/ui/messages/networkMessages.js +18 -0
- package/dist/ui/messages/preview.js +5 -4
- package/dist/ui/messages/session.d.ts +30 -21
- package/dist/ui/messages/session.js +48 -26
- package/dist/ui/messages/sessionCommand.d.ts +43 -0
- package/dist/ui/messages/sessionCommand.js +52 -0
- package/dist/utils/async.d.ts +17 -0
- package/dist/utils/async.js +36 -0
- package/dist/utils/color.d.ts +84 -0
- package/dist/utils/color.js +376 -0
- package/dist/utils/cssValues.d.ts +109 -0
- package/dist/utils/cssValues.js +236 -0
- package/dist/utils/http.d.ts +22 -1
- package/dist/utils/http.js +28 -9
- package/dist/utils/selectorFilters.d.ts +48 -8
- package/dist/utils/selectorFilters.js +296 -53
- package/dist/utils/shellDetection.d.ts +8 -2
- package/dist/utils/shellDetection.js +120 -33
- package/dist/utils/suggestions.d.ts +26 -0
- package/dist/utils/suggestions.js +73 -0
- package/dist/utils/taskMappings.js +10 -0
- package/dist/utils/url.d.ts +12 -2
- package/dist/utils/url.js +69 -7
- package/package.json +1 -1
- package/dist/ui/formatters/sessionFormatters.d.ts +0 -58
- package/dist/ui/formatters/sessionFormatters.js +0 -121
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `bdg dom wait`: wait in the page until elements appear, become visible,
|
|
3
|
+
* contain a text or are gone, and/or the page has loaded.
|
|
4
|
+
*
|
|
5
|
+
* The page watches itself (a MutationObserver plus a short poll for style
|
|
6
|
+
* and frame changes the observer cannot see) and answers as soon as what it
|
|
7
|
+
* shows changes; the condition is decided here ({@link isWaitConditionMet}).
|
|
8
|
+
* Each page call lasts at most {@link WAIT_SLICE_MS}, so a navigation during
|
|
9
|
+
* the wait (the page's context is destroyed) only costs a retry on the new
|
|
10
|
+
* document. `--gone` is confirmed by a second snapshot of the same settled
|
|
11
|
+
* document, so the empty document right after a navigation does not count.
|
|
12
|
+
*/
|
|
13
|
+
import { CommandError } from '../../errors/index.js';
|
|
14
|
+
import { invalidSelectorError, waitTimeoutError } from '../../errors/messages.js';
|
|
15
|
+
import { isContextLostError } from './evalHelpers.js';
|
|
16
|
+
import { DEEP_QUERY_JS, FILTER_MATCHING_JS, selectorArgsJS } from './targetNode.js';
|
|
17
|
+
import { isWaitConditionMet, needsGoneConfirmation, normalizeWaitText, } from './waitCondition.js';
|
|
18
|
+
import { createLogger } from '../../ui/logging/index.js';
|
|
19
|
+
import { delay, raceTimeout } from '../../utils/async.js';
|
|
20
|
+
import { getErrorMessage } from '../../utils/errors.js';
|
|
21
|
+
import { EXIT_CODES } from '../../utils/exitCodes.js';
|
|
22
|
+
import { filterDefined } from '../../utils/objects.js';
|
|
23
|
+
import { parseSelectorFilters } from '../../utils/selectorFilters.js';
|
|
24
|
+
const log = createLogger('dom');
|
|
25
|
+
/** Longest a single page call waits for a change */
|
|
26
|
+
const WAIT_SLICE_MS = 2000;
|
|
27
|
+
/** Pause before asking a page that could not answer (navigating) again */
|
|
28
|
+
const RETRY_DELAY_MS = 100;
|
|
29
|
+
/** Pause before the snapshot that confirms elements are gone */
|
|
30
|
+
const GONE_CONFIRM_DELAY_MS = 50;
|
|
31
|
+
/**
|
|
32
|
+
* Page-side: report what the page shows (matches, text matches, visible
|
|
33
|
+
* ones, readyState) as soon as it differs from `previous` (at once when
|
|
34
|
+
* `previous` is null), or after `sliceMs` without a change.
|
|
35
|
+
*/
|
|
36
|
+
const WAIT_SNAPSHOT_JS = `async function (selector, parts, text, previous, sliceMs) {
|
|
37
|
+
const { passesAll } = (${FILTER_MATCHING_JS})([]);
|
|
38
|
+
const textFilter = text === null ? [] : [{ kind: 'has-text', text: text }];
|
|
39
|
+
const visibleFilter = [{ kind: 'visible' }];
|
|
40
|
+
const snapshot = () => {
|
|
41
|
+
const matches = selector === null ? [] : (${DEEP_QUERY_JS})(selector, parts);
|
|
42
|
+
const withText = matches.filter((el) => passesAll(el, textFilter));
|
|
43
|
+
return {
|
|
44
|
+
count: matches.length,
|
|
45
|
+
textCount: withText.length,
|
|
46
|
+
visibleCount: withText.filter((el) => passesAll(el, visibleFilter)).length,
|
|
47
|
+
readyState: document.readyState,
|
|
48
|
+
documentId: performance.timeOrigin
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
const changed = (s) => previous === null || s.count !== previous.count ||
|
|
52
|
+
s.textCount !== previous.textCount || s.visibleCount !== previous.visibleCount ||
|
|
53
|
+
s.readyState !== previous.readyState || s.documentId !== previous.documentId;
|
|
54
|
+
let current = snapshot();
|
|
55
|
+
if (changed(current)) return current;
|
|
56
|
+
return await new Promise((resolve) => {
|
|
57
|
+
let scheduled = null;
|
|
58
|
+
const observer = new MutationObserver(() => { if (scheduled === null) scheduled = setTimeout(check, 16); });
|
|
59
|
+
const poll = setInterval(check, 100);
|
|
60
|
+
const slice = setTimeout(finish, sliceMs);
|
|
61
|
+
function check() {
|
|
62
|
+
scheduled = null;
|
|
63
|
+
current = snapshot();
|
|
64
|
+
if (changed(current)) finish();
|
|
65
|
+
}
|
|
66
|
+
function finish() {
|
|
67
|
+
observer.disconnect();
|
|
68
|
+
clearInterval(poll);
|
|
69
|
+
clearTimeout(slice);
|
|
70
|
+
clearTimeout(scheduled);
|
|
71
|
+
document.removeEventListener('readystatechange', check);
|
|
72
|
+
resolve(current);
|
|
73
|
+
}
|
|
74
|
+
observer.observe(document, { childList: true, subtree: true, attributes: true, characterData: true });
|
|
75
|
+
document.addEventListener('readystatechange', check);
|
|
76
|
+
});
|
|
77
|
+
}`;
|
|
78
|
+
/**
|
|
79
|
+
* Wait until the page meets the condition.
|
|
80
|
+
*
|
|
81
|
+
* @param cdp - CDP connection
|
|
82
|
+
* @param params - Condition and timeout
|
|
83
|
+
* @returns What the page showed when it was met, and how long it took
|
|
84
|
+
* @throws CommandError (81) for an invalid selector, (102) when the timeout passed;
|
|
85
|
+
* CDP errors other than a lost page context (a closed connection) as they are
|
|
86
|
+
*/
|
|
87
|
+
export async function waitForCondition(cdp, params) {
|
|
88
|
+
const condition = waitConditionOf(params);
|
|
89
|
+
const started = Date.now();
|
|
90
|
+
const deadline = started + params.timeout;
|
|
91
|
+
let last;
|
|
92
|
+
let confirming = false;
|
|
93
|
+
while (Date.now() < deadline) {
|
|
94
|
+
const snapshot = await nextSnapshot(cdp, condition, confirming ? null : (last ?? null), deadline);
|
|
95
|
+
if (!snapshot) {
|
|
96
|
+
confirming = false;
|
|
97
|
+
await delay(RETRY_DELAY_MS);
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
if (isWaitConditionMet(snapshot, condition, last)) {
|
|
101
|
+
return {
|
|
102
|
+
...condition,
|
|
103
|
+
elapsedMs: Date.now() - started,
|
|
104
|
+
...snapshotData(snapshot, condition),
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
last = snapshot;
|
|
108
|
+
confirming = needsGoneConfirmation(snapshot, condition);
|
|
109
|
+
if (confirming)
|
|
110
|
+
await delay(GONE_CONFIRM_DELAY_MS);
|
|
111
|
+
}
|
|
112
|
+
const err = waitTimeoutError(condition, last, params.timeout);
|
|
113
|
+
throw new CommandError(err.message, { suggestion: err.suggestion }, EXIT_CODES.CDP_TIMEOUT);
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* The condition of a request (options that are off left out).
|
|
117
|
+
*
|
|
118
|
+
* @param params - Request
|
|
119
|
+
* @returns Condition
|
|
120
|
+
*/
|
|
121
|
+
function waitConditionOf(params) {
|
|
122
|
+
return {
|
|
123
|
+
...filterDefined({ selector: params.selector, text: params.text }),
|
|
124
|
+
...(params.gone && { gone: true }),
|
|
125
|
+
...(params.visible && { visible: true }),
|
|
126
|
+
...(params.load && { load: true }),
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* The snapshot as reported: `textCount` only when a text was given.
|
|
131
|
+
*
|
|
132
|
+
* @param snapshot - What the page showed
|
|
133
|
+
* @param condition - What was waited for
|
|
134
|
+
* @returns Snapshot fields of the result
|
|
135
|
+
*/
|
|
136
|
+
function snapshotData(snapshot, condition) {
|
|
137
|
+
return {
|
|
138
|
+
count: snapshot.count,
|
|
139
|
+
...(condition.text !== undefined && { textCount: snapshot.textCount }),
|
|
140
|
+
visibleCount: snapshot.visibleCount,
|
|
141
|
+
readyState: snapshot.readyState,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Ask the page for its next snapshot.
|
|
146
|
+
*
|
|
147
|
+
* @param cdp - CDP connection
|
|
148
|
+
* @param condition - What is waited for
|
|
149
|
+
* @param previous - Last snapshot (null for the first call)
|
|
150
|
+
* @param deadline - When the wait ends (epoch ms)
|
|
151
|
+
* @returns The snapshot, or undefined when the page could not answer (its context was lost
|
|
152
|
+
* to a navigation, or the time ran out)
|
|
153
|
+
* @throws CommandError (81) for an invalid selector; other CDP errors (a closed connection) as they are
|
|
154
|
+
*/
|
|
155
|
+
async function nextSnapshot(cdp, condition, previous, deadline) {
|
|
156
|
+
const remaining = deadline - Date.now();
|
|
157
|
+
const selectorArgs = condition.selector === undefined ? 'null, null' : selectorArgsJS(condition.selector);
|
|
158
|
+
const text = condition.text === undefined ? null : normalizeWaitText(condition.text);
|
|
159
|
+
const args = `${selectorArgs}, ${JSON.stringify(text)}, ${JSON.stringify(previous)}, ${Math.min(remaining, WAIT_SLICE_MS)}`;
|
|
160
|
+
const evaluated = cdp.send('Runtime.evaluate', {
|
|
161
|
+
expression: `(${WAIT_SNAPSHOT_JS})(${args})`,
|
|
162
|
+
awaitPromise: true,
|
|
163
|
+
returnByValue: true,
|
|
164
|
+
});
|
|
165
|
+
evaluated.catch(() => undefined);
|
|
166
|
+
const response = await raceTimeout(evaluated, remaining).catch((error) => {
|
|
167
|
+
if (!isContextLostError(error))
|
|
168
|
+
throw error;
|
|
169
|
+
log.debug(`dom wait: page context lost (${getErrorMessage(error)}), retrying`);
|
|
170
|
+
return undefined;
|
|
171
|
+
});
|
|
172
|
+
if (!response)
|
|
173
|
+
return undefined;
|
|
174
|
+
if (response.exceptionDetails)
|
|
175
|
+
return pageException(response.exceptionDetails, condition);
|
|
176
|
+
return response.result.value;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Handle an exception of the page script: an invalid selector is the
|
|
180
|
+
* user's; anything else (a document being replaced) is retried.
|
|
181
|
+
*
|
|
182
|
+
* @param details - Exception details
|
|
183
|
+
* @param condition - What is waited for
|
|
184
|
+
* @returns undefined (retry)
|
|
185
|
+
* @throws CommandError (81) for an invalid selector
|
|
186
|
+
*/
|
|
187
|
+
function pageException(details, condition) {
|
|
188
|
+
const description = details.exception?.description ?? details.text;
|
|
189
|
+
if (condition.selector !== undefined && description.startsWith('SyntaxError')) {
|
|
190
|
+
const detail = parseSelectorFilters(condition.selector)
|
|
191
|
+
? undefined
|
|
192
|
+
: description.split('\n')[0];
|
|
193
|
+
const err = invalidSelectorError(condition.selector, detail);
|
|
194
|
+
throw new CommandError(err.message, { suggestion: err.suggestion }, EXIT_CODES.INVALID_ARGUMENTS);
|
|
195
|
+
}
|
|
196
|
+
log.debug(`dom wait: page script failed (${description.split('\n')[0]}), retrying`);
|
|
197
|
+
return undefined;
|
|
198
|
+
}
|
|
199
|
+
//# sourceMappingURL=wait.js.map
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What `bdg dom wait` waits for, and whether what the page shows meets it.
|
|
3
|
+
*
|
|
4
|
+
* The page reports a {@link WaitSnapshot} whenever it changes; the condition
|
|
5
|
+
* is decided here, in one place.
|
|
6
|
+
*/
|
|
7
|
+
/** What to wait for */
|
|
8
|
+
export interface WaitCondition {
|
|
9
|
+
/** Selector (filters like :has-text and :visible allowed); absent when only waiting for the load */
|
|
10
|
+
selector?: string;
|
|
11
|
+
/** Text one of the matches must contain (case-insensitive, whitespace collapsed) */
|
|
12
|
+
text?: string;
|
|
13
|
+
/** Wait for the (visible, with `visible`) matches to be gone */
|
|
14
|
+
gone?: boolean;
|
|
15
|
+
/** Only count visible matches */
|
|
16
|
+
visible?: boolean;
|
|
17
|
+
/** Also wait for `document.readyState` to be `complete` */
|
|
18
|
+
load?: boolean;
|
|
19
|
+
}
|
|
20
|
+
/** What the page shows at one moment */
|
|
21
|
+
export interface WaitSnapshot {
|
|
22
|
+
/** Elements matching the selector */
|
|
23
|
+
count: number;
|
|
24
|
+
/** Of those, the ones containing the text (all of them without a text) */
|
|
25
|
+
textCount: number;
|
|
26
|
+
/** Of the text matches, the visible ones */
|
|
27
|
+
visibleCount: number;
|
|
28
|
+
/** `document.readyState` */
|
|
29
|
+
readyState: string;
|
|
30
|
+
/** The document's identity (`performance.timeOrigin`): a new document after a navigation has another */
|
|
31
|
+
documentId: number;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* The text as the page compares it: whitespace collapsed, lower case (like `:has-text`).
|
|
35
|
+
*
|
|
36
|
+
* @param text - Text as given
|
|
37
|
+
* @returns Normalized text
|
|
38
|
+
*/
|
|
39
|
+
export declare function normalizeWaitText(text: string): string;
|
|
40
|
+
/**
|
|
41
|
+
* The matches the condition counts: visible ones with `visible`, else those
|
|
42
|
+
* containing the text (all matches without a text).
|
|
43
|
+
*
|
|
44
|
+
* @param snapshot - What the page shows
|
|
45
|
+
* @param condition - What is waited for
|
|
46
|
+
* @returns Number of counted matches
|
|
47
|
+
*/
|
|
48
|
+
export declare function countedMatches(snapshot: WaitSnapshot, condition: WaitCondition): number;
|
|
49
|
+
/**
|
|
50
|
+
* Whether the page shows what is waited for.
|
|
51
|
+
*
|
|
52
|
+
* Elements are gone only when two consecutive snapshots of the same
|
|
53
|
+
* document, neither of it still `loading`, show none: right after a
|
|
54
|
+
* navigation commits, the new document is empty for a moment.
|
|
55
|
+
*
|
|
56
|
+
* @param snapshot - What the page shows
|
|
57
|
+
* @param condition - What is waited for
|
|
58
|
+
* @param previous - The snapshot before it, if any
|
|
59
|
+
* @returns True when the elements are there (or gone with `gone`) and, with `load`, the page loaded
|
|
60
|
+
*/
|
|
61
|
+
export declare function isWaitConditionMet(snapshot: WaitSnapshot, condition: WaitCondition, previous?: WaitSnapshot): boolean;
|
|
62
|
+
/**
|
|
63
|
+
* Whether a `--gone` wait should confirm this snapshot with another one: it
|
|
64
|
+
* shows no (counted) matches in a document past `loading`.
|
|
65
|
+
*
|
|
66
|
+
* @param snapshot - What the page shows
|
|
67
|
+
* @param condition - What is waited for
|
|
68
|
+
* @returns True when one more snapshot of the same document may meet the condition
|
|
69
|
+
*/
|
|
70
|
+
export declare function needsGoneConfirmation(snapshot: WaitSnapshot, condition: WaitCondition): boolean;
|
|
71
|
+
//# sourceMappingURL=waitCondition.d.ts.map
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What `bdg dom wait` waits for, and whether what the page shows meets it.
|
|
3
|
+
*
|
|
4
|
+
* The page reports a {@link WaitSnapshot} whenever it changes; the condition
|
|
5
|
+
* is decided here, in one place.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* The text as the page compares it: whitespace collapsed, lower case (like `:has-text`).
|
|
9
|
+
*
|
|
10
|
+
* @param text - Text as given
|
|
11
|
+
* @returns Normalized text
|
|
12
|
+
*/
|
|
13
|
+
export function normalizeWaitText(text) {
|
|
14
|
+
return text.replace(/\s+/g, ' ').trim().toLowerCase();
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* The matches the condition counts: visible ones with `visible`, else those
|
|
18
|
+
* containing the text (all matches without a text).
|
|
19
|
+
*
|
|
20
|
+
* @param snapshot - What the page shows
|
|
21
|
+
* @param condition - What is waited for
|
|
22
|
+
* @returns Number of counted matches
|
|
23
|
+
*/
|
|
24
|
+
export function countedMatches(snapshot, condition) {
|
|
25
|
+
return condition.visible ? snapshot.visibleCount : snapshot.textCount;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Whether the page shows what is waited for.
|
|
29
|
+
*
|
|
30
|
+
* Elements are gone only when two consecutive snapshots of the same
|
|
31
|
+
* document, neither of it still `loading`, show none: right after a
|
|
32
|
+
* navigation commits, the new document is empty for a moment.
|
|
33
|
+
*
|
|
34
|
+
* @param snapshot - What the page shows
|
|
35
|
+
* @param condition - What is waited for
|
|
36
|
+
* @param previous - The snapshot before it, if any
|
|
37
|
+
* @returns True when the elements are there (or gone with `gone`) and, with `load`, the page loaded
|
|
38
|
+
*/
|
|
39
|
+
export function isWaitConditionMet(snapshot, condition, previous) {
|
|
40
|
+
if (condition.load && snapshot.readyState !== 'complete')
|
|
41
|
+
return false;
|
|
42
|
+
if (condition.selector === undefined)
|
|
43
|
+
return true;
|
|
44
|
+
if (!condition.gone)
|
|
45
|
+
return countedMatches(snapshot, condition) > 0;
|
|
46
|
+
if (previous?.documentId !== snapshot.documentId)
|
|
47
|
+
return false;
|
|
48
|
+
return showsNoneSettled(previous, condition) && showsNoneSettled(snapshot, condition);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Whether a `--gone` wait should confirm this snapshot with another one: it
|
|
52
|
+
* shows no (counted) matches in a document past `loading`.
|
|
53
|
+
*
|
|
54
|
+
* @param snapshot - What the page shows
|
|
55
|
+
* @param condition - What is waited for
|
|
56
|
+
* @returns True when one more snapshot of the same document may meet the condition
|
|
57
|
+
*/
|
|
58
|
+
export function needsGoneConfirmation(snapshot, condition) {
|
|
59
|
+
if (!condition.gone || condition.selector === undefined)
|
|
60
|
+
return false;
|
|
61
|
+
if (condition.load && snapshot.readyState !== 'complete')
|
|
62
|
+
return false;
|
|
63
|
+
return showsNoneSettled(snapshot, condition);
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* No counted matches, in a document that is no longer `loading`.
|
|
67
|
+
*
|
|
68
|
+
* @param snapshot - What the page shows
|
|
69
|
+
* @param condition - What is waited for
|
|
70
|
+
* @returns True when nothing counted matches in a settled document
|
|
71
|
+
*/
|
|
72
|
+
function showsNoneSettled(snapshot, condition) {
|
|
73
|
+
return snapshot.readyState !== 'loading' && countedMatches(snapshot, condition) === 0;
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=waitCondition.js.map
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Page emulation a session was started with: the viewport size
|
|
3
|
+
* (`--viewport`) and `prefers-color-scheme` (`--color-scheme`).
|
|
4
|
+
*
|
|
5
|
+
* The overrides belong to the session's CDP connection: they last across
|
|
6
|
+
* navigations and reloads, and Chrome drops them when the session ends, so
|
|
7
|
+
* an attached Chrome (`--chrome-ws-url`) gets its own settings back.
|
|
8
|
+
*/
|
|
9
|
+
import type { CDPConnection } from '../../connection/cdp.js';
|
|
10
|
+
import type { ColorScheme, ViewportSize } from '../../types.js';
|
|
11
|
+
/**
|
|
12
|
+
* `Emulation.setDeviceMetricsOverride` parameters for a session viewport: a
|
|
13
|
+
* desktop viewport of that size at the display's own pixel ratio.
|
|
14
|
+
*
|
|
15
|
+
* @param viewport - Viewport size in CSS px
|
|
16
|
+
* @param deviceScaleFactor - Pixel ratio (0 keeps the display's)
|
|
17
|
+
* @returns CDP parameters
|
|
18
|
+
*/
|
|
19
|
+
export declare function viewportOverride(viewport: ViewportSize, deviceScaleFactor?: number): {
|
|
20
|
+
width: number;
|
|
21
|
+
height: number;
|
|
22
|
+
deviceScaleFactor: number;
|
|
23
|
+
mobile: false;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Apply the session's emulation to its page.
|
|
27
|
+
*
|
|
28
|
+
* @param cdp - Session connection
|
|
29
|
+
* @param emulation - Viewport and color scheme, when given at start
|
|
30
|
+
*/
|
|
31
|
+
export declare function applySessionEmulation(cdp: CDPConnection, emulation: {
|
|
32
|
+
viewport?: ViewportSize;
|
|
33
|
+
colorScheme?: ColorScheme;
|
|
34
|
+
}): Promise<void>;
|
|
35
|
+
/** Page emulation of a session: what `--viewport` and `--color-scheme` set */
|
|
36
|
+
export interface SessionEmulation {
|
|
37
|
+
viewport?: ViewportSize;
|
|
38
|
+
colorScheme?: ColorScheme;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Change the page emulation mid-session (`bdg page emulate`): set the
|
|
42
|
+
* viewport or the color scheme, or clear both (back to the browser window
|
|
43
|
+
* and the system setting).
|
|
44
|
+
*
|
|
45
|
+
* @param cdp - Session connection
|
|
46
|
+
* @param current - Emulation now in effect
|
|
47
|
+
* @param change - What to set, or `reset`
|
|
48
|
+
* @param record - Called after each change Chrome accepted (so a later
|
|
49
|
+
* failure leaves the recorded emulation true)
|
|
50
|
+
* @returns Emulation in effect afterwards
|
|
51
|
+
*/
|
|
52
|
+
export declare function emulatePage(cdp: CDPConnection, current: SessionEmulation, change: SessionEmulation & {
|
|
53
|
+
reset?: boolean;
|
|
54
|
+
}, record: (emulation: SessionEmulation) => void): Promise<SessionEmulation>;
|
|
55
|
+
/** What the page currently renders with, for `bdg status` */
|
|
56
|
+
export interface PageAppearance {
|
|
57
|
+
/** Layout viewport without scrollbars (as `dom layout` reports it) */
|
|
58
|
+
viewport?: ViewportSize;
|
|
59
|
+
/** `prefers-color-scheme` the page sees */
|
|
60
|
+
colorScheme?: ColorScheme;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* The viewport and color scheme the page renders with. A busy page (or one
|
|
64
|
+
* still navigating) does not answer within {@link APPEARANCE_TIMEOUT_MS}; then
|
|
65
|
+
* nothing is reported rather than holding up `bdg status`.
|
|
66
|
+
*
|
|
67
|
+
* @param cdp - Session connection
|
|
68
|
+
* @returns Viewport and color scheme, or nothing
|
|
69
|
+
*/
|
|
70
|
+
export declare function pageAppearance(cdp: CDPConnection): Promise<PageAppearance>;
|
|
71
|
+
//# sourceMappingURL=emulation.d.ts.map
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Page emulation a session was started with: the viewport size
|
|
3
|
+
* (`--viewport`) and `prefers-color-scheme` (`--color-scheme`).
|
|
4
|
+
*
|
|
5
|
+
* The overrides belong to the session's CDP connection: they last across
|
|
6
|
+
* navigations and reloads, and Chrome drops them when the session ends, so
|
|
7
|
+
* an attached Chrome (`--chrome-ws-url`) gets its own settings back.
|
|
8
|
+
*/
|
|
9
|
+
import { VIEWPORT_SIZE_JS } from '../dom/elementGeometry.js';
|
|
10
|
+
import { createLogger } from '../../ui/logging/index.js';
|
|
11
|
+
import { getErrorMessage } from '../../utils/errors.js';
|
|
12
|
+
const log = createLogger('session');
|
|
13
|
+
/**
|
|
14
|
+
* `Emulation.setDeviceMetricsOverride` parameters for a session viewport: a
|
|
15
|
+
* desktop viewport of that size at the display's own pixel ratio.
|
|
16
|
+
*
|
|
17
|
+
* @param viewport - Viewport size in CSS px
|
|
18
|
+
* @param deviceScaleFactor - Pixel ratio (0 keeps the display's)
|
|
19
|
+
* @returns CDP parameters
|
|
20
|
+
*/
|
|
21
|
+
export function viewportOverride(viewport, deviceScaleFactor = 0) {
|
|
22
|
+
return { width: viewport.width, height: viewport.height, deviceScaleFactor, mobile: false };
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Apply the session's emulation to its page.
|
|
26
|
+
*
|
|
27
|
+
* @param cdp - Session connection
|
|
28
|
+
* @param emulation - Viewport and color scheme, when given at start
|
|
29
|
+
*/
|
|
30
|
+
export async function applySessionEmulation(cdp, emulation) {
|
|
31
|
+
if (emulation.viewport) {
|
|
32
|
+
await cdp.send('Emulation.setDeviceMetricsOverride', viewportOverride(emulation.viewport));
|
|
33
|
+
}
|
|
34
|
+
if (emulation.colorScheme) {
|
|
35
|
+
await cdp.send('Emulation.setEmulatedMedia', {
|
|
36
|
+
features: [{ name: 'prefers-color-scheme', value: emulation.colorScheme }],
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Change the page emulation mid-session (`bdg page emulate`): set the
|
|
42
|
+
* viewport or the color scheme, or clear both (back to the browser window
|
|
43
|
+
* and the system setting).
|
|
44
|
+
*
|
|
45
|
+
* @param cdp - Session connection
|
|
46
|
+
* @param current - Emulation now in effect
|
|
47
|
+
* @param change - What to set, or `reset`
|
|
48
|
+
* @param record - Called after each change Chrome accepted (so a later
|
|
49
|
+
* failure leaves the recorded emulation true)
|
|
50
|
+
* @returns Emulation in effect afterwards
|
|
51
|
+
*/
|
|
52
|
+
export async function emulatePage(cdp, current, change, record) {
|
|
53
|
+
let state = current;
|
|
54
|
+
const step = async (send, next) => {
|
|
55
|
+
await send();
|
|
56
|
+
state = next;
|
|
57
|
+
record(state);
|
|
58
|
+
};
|
|
59
|
+
const { viewport: _viewport, colorScheme, ...rest } = state;
|
|
60
|
+
if (change.reset || change.viewport) {
|
|
61
|
+
await step(() => change.viewport
|
|
62
|
+
? cdp.send('Emulation.setDeviceMetricsOverride', viewportOverride(change.viewport))
|
|
63
|
+
: cdp.send('Emulation.clearDeviceMetricsOverride', {}), {
|
|
64
|
+
...rest,
|
|
65
|
+
...(colorScheme && { colorScheme }),
|
|
66
|
+
...(change.viewport && { viewport: change.viewport }),
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
if (change.reset || change.colorScheme) {
|
|
70
|
+
const { colorScheme: _scheme, ...withoutScheme } = state;
|
|
71
|
+
await step(() => cdp.send('Emulation.setEmulatedMedia', {
|
|
72
|
+
features: [{ name: 'prefers-color-scheme', value: change.colorScheme ?? '' }],
|
|
73
|
+
}), { ...withoutScheme, ...(change.colorScheme && { colorScheme: change.colorScheme }) });
|
|
74
|
+
}
|
|
75
|
+
return state;
|
|
76
|
+
}
|
|
77
|
+
/** How long `bdg status` waits for the page to report its appearance */
|
|
78
|
+
const APPEARANCE_TIMEOUT_MS = 1000;
|
|
79
|
+
/**
|
|
80
|
+
* The viewport and color scheme the page renders with. A busy page (or one
|
|
81
|
+
* still navigating) does not answer within {@link APPEARANCE_TIMEOUT_MS}; then
|
|
82
|
+
* nothing is reported rather than holding up `bdg status`.
|
|
83
|
+
*
|
|
84
|
+
* @param cdp - Session connection
|
|
85
|
+
* @returns Viewport and color scheme, or nothing
|
|
86
|
+
*/
|
|
87
|
+
export async function pageAppearance(cdp) {
|
|
88
|
+
const expression = `(() => {
|
|
89
|
+
const size = (${VIEWPORT_SIZE_JS})(window);
|
|
90
|
+
return { width: Math.round(size.width), height: Math.round(size.height), dark: matchMedia('(prefers-color-scheme: dark)').matches };
|
|
91
|
+
})()`;
|
|
92
|
+
let timer;
|
|
93
|
+
const timeout = new Promise((resolve) => {
|
|
94
|
+
timer = setTimeout(() => resolve(null), APPEARANCE_TIMEOUT_MS);
|
|
95
|
+
});
|
|
96
|
+
try {
|
|
97
|
+
const response = (await Promise.race([
|
|
98
|
+
cdp.send('Runtime.evaluate', { expression, returnByValue: true }),
|
|
99
|
+
timeout,
|
|
100
|
+
]));
|
|
101
|
+
const value = response?.result?.value;
|
|
102
|
+
if (!value)
|
|
103
|
+
return {};
|
|
104
|
+
return {
|
|
105
|
+
viewport: { width: value.width, height: value.height },
|
|
106
|
+
colorScheme: value.dark ? 'dark' : 'light',
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
catch (error) {
|
|
110
|
+
log.debug(`Could not read the page's viewport and color scheme: ${getErrorMessage(error)}`);
|
|
111
|
+
return {};
|
|
112
|
+
}
|
|
113
|
+
finally {
|
|
114
|
+
clearTimeout(timer);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=emulation.js.map
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether the page finished loading, and what it is still waiting for.
|
|
3
|
+
*
|
|
4
|
+
* Start and `bdg page` wait a limited time for the page; a document still
|
|
5
|
+
* `loading` after that (a script whose server never answers) makes every
|
|
6
|
+
* later command misleading, so they report it with the requests still
|
|
7
|
+
* running.
|
|
8
|
+
*/
|
|
9
|
+
import type { PendingRequestInfo, PageLoadingState } from '../../ipc/protocol/commands.js';
|
|
10
|
+
import type { PendingRequest } from '../../telemetry/network.js';
|
|
11
|
+
import type { CDPSender } from '../../telemetry/objectExpander.js';
|
|
12
|
+
/**
|
|
13
|
+
* The requests to name: load-blocking ones (scripts, styles, images, frames)
|
|
14
|
+
* first, the longest-running first within each group.
|
|
15
|
+
*
|
|
16
|
+
* @param pending - Requests still running
|
|
17
|
+
* @param now - Current time (ms)
|
|
18
|
+
* @returns Up to {@link MAX_NAMED_PENDING_REQUESTS} requests
|
|
19
|
+
*/
|
|
20
|
+
export declare function summarizePendingRequests(pending: Iterable<PendingRequest>, now: number): PendingRequestInfo[];
|
|
21
|
+
/**
|
|
22
|
+
* The page's `document.readyState`.
|
|
23
|
+
*
|
|
24
|
+
* @param cdp - CDP connection
|
|
25
|
+
* @returns The state, or undefined when the page did not answer in time
|
|
26
|
+
*/
|
|
27
|
+
export declare function readDocumentReadyState(cdp: CDPSender): Promise<string | undefined>;
|
|
28
|
+
/**
|
|
29
|
+
* What the page is still loading, if it has not finished.
|
|
30
|
+
*
|
|
31
|
+
* @param cdp - CDP connection
|
|
32
|
+
* @param pending - Requests still running (from the session's network telemetry)
|
|
33
|
+
* @returns The loading state, or undefined when the document is complete or did not answer
|
|
34
|
+
*/
|
|
35
|
+
export declare function readPageLoadingState(cdp: CDPSender, pending: Iterable<PendingRequest>): Promise<PageLoadingState | undefined>;
|
|
36
|
+
//# sourceMappingURL=loadingState.d.ts.map
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether the page finished loading, and what it is still waiting for.
|
|
3
|
+
*
|
|
4
|
+
* Start and `bdg page` wait a limited time for the page; a document still
|
|
5
|
+
* `loading` after that (a script whose server never answers) makes every
|
|
6
|
+
* later command misleading, so they report it with the requests still
|
|
7
|
+
* running.
|
|
8
|
+
*/
|
|
9
|
+
import { createLogger } from '../../ui/logging/index.js';
|
|
10
|
+
import { raceTimeout } from '../../utils/async.js';
|
|
11
|
+
import { getErrorMessage } from '../../utils/errors.js';
|
|
12
|
+
const log = createLogger('readiness');
|
|
13
|
+
/** Pending requests named in the report */
|
|
14
|
+
const MAX_NAMED_PENDING_REQUESTS = 3;
|
|
15
|
+
/** How long the page gets to report its readyState (a busy page is not waited on) */
|
|
16
|
+
const READY_STATE_TIMEOUT_MS = 1000;
|
|
17
|
+
/** Request types that never hold up the load event, listed after the ones that do */
|
|
18
|
+
const NON_BLOCKING_TYPES = new Set([
|
|
19
|
+
'XHR',
|
|
20
|
+
'Fetch',
|
|
21
|
+
'EventSource',
|
|
22
|
+
'Ping',
|
|
23
|
+
'Preflight',
|
|
24
|
+
'Prefetch',
|
|
25
|
+
]);
|
|
26
|
+
/**
|
|
27
|
+
* The requests to name: load-blocking ones (scripts, styles, images, frames)
|
|
28
|
+
* first, the longest-running first within each group.
|
|
29
|
+
*
|
|
30
|
+
* @param pending - Requests still running
|
|
31
|
+
* @param now - Current time (ms)
|
|
32
|
+
* @returns Up to {@link MAX_NAMED_PENDING_REQUESTS} requests
|
|
33
|
+
*/
|
|
34
|
+
export function summarizePendingRequests(pending, now) {
|
|
35
|
+
const rank = (entry) => NON_BLOCKING_TYPES.has(entry.request.resourceType ?? '') ? 1 : 0;
|
|
36
|
+
return [...pending]
|
|
37
|
+
.sort((a, b) => rank(a) - rank(b) || a.request.timestamp - b.request.timestamp)
|
|
38
|
+
.slice(0, MAX_NAMED_PENDING_REQUESTS)
|
|
39
|
+
.map(({ request }) => ({
|
|
40
|
+
method: request.method,
|
|
41
|
+
url: request.url,
|
|
42
|
+
...(request.resourceType && { resourceType: request.resourceType }),
|
|
43
|
+
pendingMs: Math.max(0, now - request.timestamp),
|
|
44
|
+
}));
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* The page's `document.readyState`.
|
|
48
|
+
*
|
|
49
|
+
* @param cdp - CDP connection
|
|
50
|
+
* @returns The state, or undefined when the page did not answer in time
|
|
51
|
+
*/
|
|
52
|
+
export async function readDocumentReadyState(cdp) {
|
|
53
|
+
try {
|
|
54
|
+
const evaluated = cdp.send('Runtime.evaluate', {
|
|
55
|
+
expression: 'document.readyState',
|
|
56
|
+
returnByValue: true,
|
|
57
|
+
});
|
|
58
|
+
evaluated.catch(() => undefined);
|
|
59
|
+
const response = await raceTimeout(evaluated, READY_STATE_TIMEOUT_MS);
|
|
60
|
+
const value = response?.result?.value;
|
|
61
|
+
return typeof value === 'string' ? value : undefined;
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
log.debug(`Could not read document.readyState: ${getErrorMessage(error)}`);
|
|
65
|
+
return undefined;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* What the page is still loading, if it has not finished.
|
|
70
|
+
*
|
|
71
|
+
* @param cdp - CDP connection
|
|
72
|
+
* @param pending - Requests still running (from the session's network telemetry)
|
|
73
|
+
* @returns The loading state, or undefined when the document is complete or did not answer
|
|
74
|
+
*/
|
|
75
|
+
export async function readPageLoadingState(cdp, pending) {
|
|
76
|
+
const readyState = await readDocumentReadyState(cdp);
|
|
77
|
+
if (readyState === undefined || readyState === 'complete')
|
|
78
|
+
return undefined;
|
|
79
|
+
const requests = [...pending];
|
|
80
|
+
return {
|
|
81
|
+
readyState,
|
|
82
|
+
pending: summarizePendingRequests(requests, Date.now()),
|
|
83
|
+
pendingCount: requests.length,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=loadingState.js.map
|