diff --git a/.vscode/settings.json b/.vscode/settings.json index 411c3c43e..535ec0d82 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -17,5 +17,6 @@ "editor.codeActionsOnSave": { "source.organizeImports": true }, - "editor.formatOnSave": true + "editor.formatOnSave": true, + "python.formatting.provider": "black" } diff --git a/CHANGELOG.md b/CHANGELOG.md index acad7b1c0..6414469e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ This changelog records changes to stable releases since 1.50.2. "TBA" changes he ## TBA +- feat: allow debugging node worker_threads - feat: make the line on log messages take into account skipFiles ([#882](https://github.com/microsoft/vscode-js-debug/issues/882)) - fix: persist state in the diagnostic tool ([#879](https://github.com/microsoft/vscode-js-debug/issues/879)) - fix: allow outdated node dialog to be bypassed ([ref](https://github.com/microsoft/vscode/issues/111642)) diff --git a/package.json b/package.json index ca6e9f46a..efca47b9a 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ "package": "gulp package", "publish": "gulp publish", "updatetypes": "cd src/typings && vscode-dts dev && vscode-dts master", + "updatenodeapi": "python src/build/getNodePdl.py && prettier --write src/build/nodeCustom.ts", "generateapis": "node out/src/build/generateDap.js && node out/src/build/generateCdp.js", "test": "gulp && npm-run-all --parallel test:unit test:types test:golden test:lint", "test:types": "tsc --noEmit", diff --git a/src/adapter/threads.ts b/src/adapter/threads.ts index ea43849ea..6b53bb1ba 100644 --- a/src/adapter/threads.ts +++ b/src/adapter/threads.ts @@ -101,11 +101,10 @@ export type Script = { export interface IThreadDelegate { name(): string; supportsCustomBreakpoints(): boolean; - shouldCheckContentHash(): boolean; scriptUrlToUrl(url: string): string; executionContextName(description: Cdp.Runtime.ExecutionContextDescription): string; initialize(): Promise; - entryBreakpoint: IBreakpointPathAndId | undefined; + entryBreakpoint?: IBreakpointPathAndId; } export type ScriptWithSourceMapHandler = ( @@ -1164,14 +1163,13 @@ export class Thread implements IVariableStoreDelegate { } } - const hash = this._delegate.shouldCheckContentHash() ? event.hash : undefined; const source = await this._sourceContainer.addSource( event.url, contentGetter, resolvedSourceMapUrl, inlineSourceOffset, runtimeScriptOffset, - hash, + event.hash, ); // eslint-disable-next-line @typescript-eslint/no-non-null-assertion diff --git a/src/binder.ts b/src/binder.ts index 1668b77d6..751a0e880 100644 --- a/src/binder.ts +++ b/src/binder.ts @@ -197,7 +197,7 @@ export class Binder implements IDisposable { return; } - await new Promise(resolve => + await new Promise(resolve => this.onTargetListChanged(() => { if (didTerminate()) { resolve(); diff --git a/src/build/.gitignore b/src/build/.gitignore new file mode 100644 index 000000000..b3f8300a7 --- /dev/null +++ b/src/build/.gitignore @@ -0,0 +1,2 @@ +/__pycache__ +/pdl.py diff --git a/src/build/generateCdp.ts b/src/build/generateCdp.ts index 1d50ade83..b42ac151e 100644 --- a/src/build/generateCdp.ts +++ b/src/build/generateCdp.ts @@ -2,15 +2,16 @@ * Copyright (C) Microsoft Corporation. All rights reserved. *--------------------------------------------------------*/ -import { writeCodeToFile, autoGeneratedFileHeader } from './generateUtils'; import got from 'got'; +import { autoGeneratedFileHeader, writeCodeToFile } from './generateUtils'; +import nodeCustom from './nodeCustom'; // generic hack -- I would prefer to union/omit, ProtocolType loses the ability // to discriminate union types when passing through in that way. interface IProtocolType { name: WithId extends true ? never : string; id: WithId extends false ? never : string; - description: string; + description?: string; optional?: true; deprecated?: true; experimental?: true; @@ -62,8 +63,8 @@ interface IProtocolEvent { interface IProtocolDomain { domain: string; experimental: boolean; - dependencies: ReadonlyArray; - types: ReadonlyArray>; + dependencies?: ReadonlyArray; + types?: ReadonlyArray>; commands: ReadonlyArray; events: ReadonlyArray; } @@ -92,7 +93,10 @@ async function generate() { const compareDomains = (a: IProtocolDomain, b: IProtocolDomain) => a.domain.toUpperCase() < b.domain.toUpperCase() ? -1 : 1; - const domains = jsProtocol.domains.concat(browserProtocol.domains).sort(compareDomains); + const domains = jsProtocol.domains + .concat(browserProtocol.domains) + .concat((nodeCustom.domains as unknown) as IProtocolDomain[]) + .sort(compareDomains); const result = []; const interfaceSeparator = createSeparator(); @@ -151,7 +155,7 @@ async function generate() { const separator = createSeparator(); for (const prop of props) { separator(); - appendText(prop.description, { deprecated: !!prop.deprecated }); + appendText(prop.description ?? '', { deprecated: !!prop.deprecated }); result.push(`${prop.name}${prop.optional ? '?' : ''}: ${generateType(prop)};`); } } @@ -210,7 +214,7 @@ async function generate() { } for (const type of types) { typesSeparator(); - appendText(type.description, { deprecated: !!type.deprecated }); + appendText(type.description ?? '', { deprecated: !!type.deprecated }); if (type.type === 'object') { result.push(`export interface ${toTitleCase(type.id)} {`); if (type.properties) appendProps(type.properties); diff --git a/src/build/getNodePdl.py b/src/build/getNodePdl.py new file mode 100644 index 000000000..9861855c8 --- /dev/null +++ b/src/build/getNodePdl.py @@ -0,0 +1,18 @@ +import json +import os.path +import urllib.request + +# To use this, download and copy pdl.py from here beside this file +# https://github.com/nodejs/node/blob/e31a99f01b8a92615ce79b845441949424cd1dda/tools/inspector_protocol/pdl.py +import pdl + +inspector_pdl_url = "https://raw.githubusercontent.com/nodejs/node/master/src/inspector/node_protocol.pdl" +with urllib.request.urlopen(inspector_pdl_url) as r: + pdl_contents = pdl.loads(r.read().decode("utf-8"), "node_protocol.pdl", True) + with open(os.path.join(os.path.dirname(__file__), "nodeCustom.ts"), "w") as o: + o.write("/*---------------------------------------------------------\n") + o.write(" * Copyright (C) Microsoft Corporation. All rights reserved.\n") + o.write(" *--------------------------------------------------------*/\n") + o.write("\n") + o.write("export default ") + json.dump(pdl_contents, o, indent=2, separators=(",", ": ")) diff --git a/src/build/nodeCustom.ts b/src/build/nodeCustom.ts new file mode 100644 index 000000000..3dc768121 --- /dev/null +++ b/src/build/nodeCustom.ts @@ -0,0 +1,245 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +export default { + version: { + major: '1', + minor: '0', + }, + domains: [ + { + domain: 'NodeTracing', + experimental: true, + types: [ + { + id: 'TraceConfig', + type: 'object', + properties: [ + { + name: 'recordMode', + description: 'Controls how the trace buffer stores data.', + optional: true, + type: 'string', + enum: ['recordUntilFull', 'recordContinuously', 'recordAsMuchAsPossible'], + }, + { + name: 'includedCategories', + description: 'Included category filters.', + type: 'array', + items: { + type: 'string', + }, + }, + ], + }, + ], + commands: [ + { + name: 'getCategories', + description: 'Gets supported tracing categories.', + returns: [ + { + name: 'categories', + description: 'A list of supported tracing categories.', + type: 'array', + items: { + type: 'string', + }, + }, + ], + }, + { + name: 'start', + description: 'Start trace events collection.', + parameters: [ + { + name: 'traceConfig', + $ref: 'TraceConfig', + }, + ], + }, + { + name: 'stop', + description: + 'Stop trace events collection. Remaining collected events will be sent as a sequence of\ndataCollected events followed by tracingComplete event.', + }, + ], + events: [ + { + name: 'dataCollected', + description: 'Contains an bucket of collected trace events.', + parameters: [ + { + name: 'value', + type: 'array', + items: { + type: 'object', + }, + }, + ], + }, + { + name: 'tracingComplete', + description: + 'Signals that tracing is stopped and there is no trace buffers pending flush, all data were\ndelivered via dataCollected events.', + }, + ], + }, + { + domain: 'NodeWorker', + description: 'Support for sending messages to Node worker Inspector instances.', + experimental: true, + types: [ + { + id: 'WorkerID', + type: 'string', + }, + { + id: 'SessionID', + description: 'Unique identifier of attached debugging session.', + type: 'string', + }, + { + id: 'WorkerInfo', + type: 'object', + properties: [ + { + name: 'workerId', + $ref: 'WorkerID', + }, + { + name: 'type', + type: 'string', + }, + { + name: 'title', + type: 'string', + }, + { + name: 'url', + type: 'string', + }, + ], + }, + ], + commands: [ + { + name: 'sendMessageToWorker', + description: 'Sends protocol message over session with given id.', + parameters: [ + { + name: 'message', + type: 'string', + }, + { + name: 'sessionId', + description: 'Identifier of the session.', + $ref: 'SessionID', + }, + ], + }, + { + name: 'enable', + description: + 'Instructs the inspector to attach to running workers. Will also attach to new workers\nas they start', + parameters: [ + { + name: 'waitForDebuggerOnStart', + description: + 'Whether to new workers should be paused until the frontend sends `Runtime.runIfWaitingForDebugger`\nmessage to run them.', + type: 'boolean', + }, + ], + }, + { + name: 'disable', + description: + 'Detaches from all running workers and disables attaching to new workers as they are started.', + }, + { + name: 'detach', + description: 'Detached from the worker with given sessionId.', + parameters: [ + { + name: 'sessionId', + $ref: 'SessionID', + }, + ], + }, + ], + events: [ + { + name: 'attachedToWorker', + description: 'Issued when attached to a worker.', + parameters: [ + { + name: 'sessionId', + description: 'Identifier assigned to the session used to send/receive messages.', + $ref: 'SessionID', + }, + { + name: 'workerInfo', + $ref: 'WorkerInfo', + }, + { + name: 'waitingForDebugger', + type: 'boolean', + }, + ], + }, + { + name: 'detachedFromWorker', + description: 'Issued when detached from the worker.', + parameters: [ + { + name: 'sessionId', + description: 'Detached session identifier.', + $ref: 'SessionID', + }, + ], + }, + { + name: 'receivedMessageFromWorker', + description: + 'Notifies about a new protocol message received from the session\n(session ID is provided in attachedToWorker notification).', + parameters: [ + { + name: 'sessionId', + description: 'Identifier of a session which sends a message.', + $ref: 'SessionID', + }, + { + name: 'message', + type: 'string', + }, + ], + }, + ], + }, + { + domain: 'NodeRuntime', + description: 'Support for inspecting node process state.', + experimental: true, + commands: [ + { + name: 'notifyWhenWaitingForDisconnect', + description: 'Enable the `NodeRuntime.waitingForDisconnect`.', + parameters: [ + { + name: 'enabled', + type: 'boolean', + }, + ], + }, + ], + events: [ + { + name: 'waitingForDisconnect', + description: + 'This event is fired instead of `Runtime.executionContextDestroyed` when\nenabled.\nIt is fired when the Node process finished all code execution and is\nwaiting for all frontends to disconnect.', + }, + ], + }, + ], +}; diff --git a/src/cdp/api.d.ts b/src/cdp/api.d.ts index 86d2aa70e..3212ae881 100644 --- a/src/cdp/api.d.ts +++ b/src/cdp/api.d.ts @@ -52,6 +52,9 @@ export namespace Cdp { Media: MediaApi; Memory: MemoryApi; Network: NetworkApi; + NodeRuntime: NodeRuntimeApi; + NodeTracing: NodeTracingApi; + NodeWorker: NodeWorkerApi; Overlay: OverlayApi; Page: PageApi; Performance: PerformanceApi; @@ -92,12 +95,20 @@ export namespace Cdp { ): Promise; /** - * Fetches the entire accessibility tree + * Fetches the entire accessibility tree for the root Document */ getFullAXTree( params: Accessibility.GetFullAXTreeParams, ): Promise; + /** + * Fetches a particular accessibility node by AXNodeId. + * Requires `enable()` to have been called previously. + */ + getChildAXNodes( + params: Accessibility.GetChildAXNodesParams, + ): Promise; + /** * Query a DOM node's accessibility subtree for accessible name and role. * This command computes the name and role for all nodes in the subtree, including those that are @@ -173,7 +184,13 @@ export namespace Cdp { /** * Parameters of the 'Accessibility.getFullAXTree' method. */ - export interface GetFullAXTreeParams {} + export interface GetFullAXTreeParams { + /** + * The maximum depth at which descendants of the root node should be retrieved. + * If omitted, the full tree is returned. + */ + max_depth?: integer; + } /** * Return value of the 'Accessibility.getFullAXTree' method. @@ -182,6 +199,20 @@ export namespace Cdp { nodes: AXNode[]; } + /** + * Parameters of the 'Accessibility.getChildAXNodes' method. + */ + export interface GetChildAXNodesParams { + id: AXNodeId; + } + + /** + * Return value of the 'Accessibility.getChildAXNodes' method. + */ + export interface GetChildAXNodesResult { + nodes: AXNode[]; + } + /** * Parameters of the 'Accessibility.queryAXTree' method. */ @@ -270,6 +301,7 @@ export namespace Cdp { | 'labelfor' | 'labelwrapped' | 'legend' + | 'rubyannotation' | 'tablecaption' | 'title' | 'other'; @@ -1452,6 +1484,8 @@ export namespace Cdp { */ violatedDirective: string; + isReportOnly: boolean; + contentSecurityPolicyViolationType: ContentSecurityPolicyViolationType; frameAncestor?: AffectedFrame; @@ -1781,6 +1815,13 @@ export namespace Cdp { * Set dock tile details, platform-specific. */ setDockTile(params: Browser.SetDockTileParams): Promise; + + /** + * Invoke custom browser commands used by telemetry. + */ + executeBrowserCommand( + params: Browser.ExecuteBrowserCommandParams, + ): Promise; } /** @@ -2099,6 +2140,18 @@ export namespace Cdp { */ export interface SetDockTileResult {} + /** + * Parameters of the 'Browser.executeBrowserCommand' method. + */ + export interface ExecuteBrowserCommandParams { + commandId: BrowserCommandId; + } + + /** + * Return value of the 'Browser.executeBrowserCommand' method. + */ + export interface ExecuteBrowserCommandResult {} + export type BrowserContextID = string; export type WindowID = integer; @@ -2157,6 +2210,7 @@ export namespace Cdp { | 'protectedMediaIdentifier' | 'sensors' | 'videoCapture' + | 'videoCapturePanTiltZoom' | 'idleDetection' | 'wakeLockScreen' | 'wakeLockSystem'; @@ -2189,8 +2243,18 @@ export namespace Cdp { * For "clipboard" permission, may specify allowWithoutSanitization. */ allowWithoutSanitization?: boolean; + + /** + * For "camera" permission, may specify panTiltZoom. + */ + panTiltZoom?: boolean; } + /** + * Browser command ids used by executeBrowserCommand. + */ + export type BrowserCommandId = 'openTabSearch' | 'closeTabSearch'; + /** * Chrome histogram bucket. */ @@ -7423,6 +7487,7 @@ export namespace Cdp { | 'marker' | 'backdrop' | 'selection' + | 'target-text' | 'first-line-inherited' | 'scrollbar' | 'scrollbar-thumb' @@ -7753,6 +7818,13 @@ export namespace Cdp { params: DOMDebugger.RemoveXHRBreakpointParams, ): Promise; + /** + * Sets breakpoint on particular CSP violations. + */ + setBreakOnCSPViolation( + params: DOMDebugger.SetBreakOnCSPViolationParams, + ): Promise; + /** * Sets breakpoint on particular operation with DOM. */ @@ -7888,6 +7960,21 @@ export namespace Cdp { */ export interface RemoveXHRBreakpointResult {} + /** + * Parameters of the 'DOMDebugger.setBreakOnCSPViolation' method. + */ + export interface SetBreakOnCSPViolationParams { + /** + * CSP Violations to stop upon. + */ + violationTypes: CSPViolationType[]; + } + + /** + * Return value of the 'DOMDebugger.setBreakOnCSPViolation' method. + */ + export interface SetBreakOnCSPViolationResult {} + /** * Parameters of the 'DOMDebugger.setDOMBreakpoint' method. */ @@ -7964,6 +8051,11 @@ export namespace Cdp { */ export type DOMBreakpointType = 'subtree-modified' | 'attribute-modified' | 'node-removed'; + /** + * CSP Violation type. + */ + export type CSPViolationType = 'trustedtype-sink-violation' | 'trustedtype-policy-violation'; + /** * Object event listener. */ @@ -9032,6 +9124,10 @@ export namespace Cdp { params: Emulation.SetVisibleSizeParams, ): Promise; + setDisabledImageTypes( + params: Emulation.SetDisabledImageTypesParams, + ): Promise; + /** * Allows overriding user agent with the given string. */ @@ -9524,6 +9620,21 @@ export namespace Cdp { */ export interface SetVisibleSizeResult {} + /** + * Parameters of the 'Emulation.setDisabledImageTypes' method. + */ + export interface SetDisabledImageTypesParams { + /** + * Image types to disable. + */ + imageTypes: DisabledImageType[]; + } + + /** + * Return value of the 'Emulation.setDisabledImageTypes' method. + */ + export interface SetDisabledImageTypesResult {} + /** * Parameters of the 'Emulation.setUserAgentOverride' method. */ @@ -9635,6 +9746,11 @@ export namespace Cdp { mobile: boolean; } + + /** + * Enum of image types that can be disabled. + */ + export type DisabledImageType = 'avif' | 'webp'; } /** @@ -11283,6 +11399,31 @@ export namespace Cdp { */ clickCount?: integer; + /** + * The normalized pressure, which has a range of [0,1] (default: 0). + */ + force?: number; + + /** + * The normalized tangential pressure, which has a range of [-1,1] (default: 0). + */ + tangentialPressure?: number; + + /** + * The plane angle between the Y-Z plane and the plane containing both the stylus axis and the Y axis, in degrees of the range [-90,90], a positive tiltX is to the right (default: 0). + */ + tiltX?: integer; + + /** + * The plane angle between the X-Z plane and the plane containing both the stylus axis and the X axis, in degrees of the range [-90,90], a positive tiltY is towards the user (default: 0). + */ + tiltY?: integer; + + /** + * The clockwise rotation of a pen stylus around its own major axis, in degrees in the range [0,359] (default: 0). + */ + twist?: integer; + /** * X delta in CSS pixels for mouse wheel event (default: 0). */ @@ -11586,6 +11727,26 @@ export namespace Cdp { */ force?: number; + /** + * The normalized tangential pressure, which has a range of [-1,1] (default: 0). + */ + tangentialPressure?: number; + + /** + * The plane angle between the Y-Z plane and the plane containing both the stylus axis and the Y axis, in degrees of the range [-90,90], a positive tiltX is to the right (default: 0) + */ + tiltX?: integer; + + /** + * The plane angle between the X-Z plane and the plane containing both the stylus axis and the X axis, in degrees of the range [-90,90], a positive tiltY is towards the user (default: 0). + */ + tiltY?: integer; + + /** + * The clockwise rotation of a pen stylus around its own major axis, in degrees in the range [0,359] (default: 0). + */ + twist?: integer; + /** * Identifier used to track touch sources between events, must be unique within an event. */ @@ -13083,11 +13244,11 @@ export namespace Cdp { ): Promise; /** - * Specifies whether to sned a debug header to all outgoing requests. + * Specifies whether to attach a page script stack id in requests */ - setAttachDebugHeader( - params: Network.SetAttachDebugHeaderParams, - ): Promise; + setAttachDebugStack( + params: Network.SetAttachDebugStackParams, + ): Promise; /** * Sets the requests to intercept that match the provided patterns and optionally resource types. @@ -13252,6 +13413,19 @@ export namespace Cdp { listener: (event: Network.WebSocketWillSendHandshakeRequestEvent) => void, ): IDisposable; + /** + * Fired upon WebTransport creation. + */ + on( + event: 'webTransportCreated', + listener: (event: Network.WebTransportCreatedEvent) => void, + ): IDisposable; + + on( + event: 'webTransportClosed', + listener: (event: Network.WebTransportClosedEvent) => void, + ): IDisposable; + /** * Fired when additional information about a requestWillBeSent event is available from the * network stack. Not every requestWillBeSent event will have an additional @@ -13272,6 +13446,17 @@ export namespace Cdp { event: 'responseReceivedExtraInfo', listener: (event: Network.ResponseReceivedExtraInfoEvent) => void, ): IDisposable; + + /** + * Fired exactly once for each Trust Token operation. Depending on + * the type of the operation and whether the operation succeeded or + * failed, the event is fired before the corresponding request was sent + * or after the response was received. + */ + on( + event: 'trustTokenOperationDone', + listener: (event: Network.TrustTokenOperationDoneEvent) => void, + ): IDisposable; } /** @@ -13848,19 +14033,19 @@ export namespace Cdp { export interface SetExtraHTTPHeadersResult {} /** - * Parameters of the 'Network.setAttachDebugHeader' method. + * Parameters of the 'Network.setAttachDebugStack' method. */ - export interface SetAttachDebugHeaderParams { + export interface SetAttachDebugStackParams { /** - * Whether to send a debug header. + * Whether to attach a page script stack for debugging purpose. */ enabled: boolean; } /** - * Return value of the 'Network.setAttachDebugHeader' method. + * Return value of the 'Network.setAttachDebugStack' method. */ - export interface SetAttachDebugHeaderResult {} + export interface SetAttachDebugStackResult {} /** * Parameters of the 'Network.setRequestInterception' method. @@ -14040,6 +14225,11 @@ export namespace Cdp { * The reason why loading was blocked, if any. */ blockedReason?: BlockedReason; + + /** + * The reason why loading was blocked by CORS, if any. + */ + corsErrorStatus?: CorsErrorStatus; } /** @@ -14418,6 +14608,46 @@ export namespace Cdp { request: WebSocketRequest; } + /** + * Parameters of the 'Network.webTransportCreated' event. + */ + export interface WebTransportCreatedEvent { + /** + * WebTransport identifier. + */ + transportId: RequestId; + + /** + * WebTransport request URL. + */ + url: string; + + /** + * Timestamp. + */ + timestamp: MonotonicTime; + + /** + * Request initiator. + */ + initiator?: Initiator; + } + + /** + * Parameters of the 'Network.webTransportClosed' event. + */ + export interface WebTransportClosedEvent { + /** + * WebTransport identifier. + */ + transportId: RequestId; + + /** + * Timestamp. + */ + timestamp: MonotonicTime; + } + /** * Parameters of the 'Network.requestWillBeSentExtraInfo' event. */ @@ -14437,6 +14667,11 @@ export namespace Cdp { * Raw request headers as they will be sent over the wire. */ headers: Headers; + + /** + * The client security state set for the request. + */ + clientSecurityState?: ClientSecurityState; } /** @@ -14467,6 +14702,48 @@ export namespace Cdp { headersText?: string; } + /** + * Parameters of the 'Network.trustTokenOperationDone' event. + */ + export interface TrustTokenOperationDoneEvent { + /** + * Detailed success or error status of the operation. + * 'AlreadyExists' also signifies a successful operation, as the result + * of the operation already exists und thus, the operation was abort + * preemptively (e.g. a cache hit). + */ + status: + | 'Ok' + | 'InvalidArgument' + | 'FailedPrecondition' + | 'ResourceExhausted' + | 'AlreadyExists' + | 'Unavailable' + | 'BadResponse' + | 'InternalError' + | 'UnknownError' + | 'FulfilledLocally'; + + type: TrustTokenOperationType; + + requestId: RequestId; + + /** + * Top level origin. The context in which the operation was attempted. + */ + topLevelOrigin?: string; + + /** + * Origin of the issuer in case of a "Issuance" or "Redemption" operation. + */ + issuerOrigin?: string; + + /** + * The number of obtained Trust Tokens on a successful "Issuance" operation. + */ + issuedTokenCount?: integer; + } + /** * Resource type as it was perceived by the rendering engine. */ @@ -14486,6 +14763,7 @@ export namespace Cdp { | 'SignedExchange' | 'Ping' | 'CSPViolationReport' + | 'Preflight' | 'Other'; /** @@ -14739,6 +15017,12 @@ export namespace Cdp { * Whether is loaded via link preload. */ isLinkPreload?: boolean; + + /** + * Set for requests when the TrustToken API is used. Contains the parameters + * passed by the developer (e.g. via "fetch") as understood by the backend. + */ + trustTokenParams?: TrustTokenParams; } /** @@ -14879,6 +15163,42 @@ export namespace Cdp { | 'corp-not-same-origin-after-defaulted-to-same-origin-by-coep' | 'corp-not-same-site'; + /** + * The reason why request was blocked. + */ + export type CorsError = + | 'DisallowedByMode' + | 'InvalidResponse' + | 'WildcardOriginNotAllowed' + | 'MissingAllowOriginHeader' + | 'MultipleAllowOriginValues' + | 'InvalidAllowOriginValue' + | 'AllowOriginMismatch' + | 'InvalidAllowCredentials' + | 'CorsDisabledScheme' + | 'PreflightInvalidStatus' + | 'PreflightDisallowedRedirect' + | 'PreflightWildcardOriginNotAllowed' + | 'PreflightMissingAllowOriginHeader' + | 'PreflightMultipleAllowOriginValues' + | 'PreflightInvalidAllowOriginValue' + | 'PreflightAllowOriginMismatch' + | 'PreflightInvalidAllowCredentials' + | 'PreflightMissingAllowExternal' + | 'PreflightInvalidAllowExternal' + | 'InvalidAllowMethodsPreflightResponse' + | 'InvalidAllowHeadersPreflightResponse' + | 'MethodDisallowedByPreflightResponse' + | 'HeaderDisallowedByPreflightResponse' + | 'RedirectContainsCredentials' + | 'InsecurePrivateNetwork'; + + export interface CorsErrorStatus { + corsError: CorsError; + + failedParameter: string; + } + /** * Source of serviceworker response. */ @@ -14888,6 +15208,29 @@ export namespace Cdp { | 'fallback-code' | 'network'; + /** + * Determines what type of Trust Token operation is executed and + * depending on the type, some additional parameters. The values + * are specified in third_party/blink/renderer/core/fetch/trust_token.idl. + */ + export interface TrustTokenParams { + type: TrustTokenOperationType; + + /** + * Only set for "token-redemption" type and determine whether + * to request a fresh SRR or use a still valid cached SRR. + */ + refreshPolicy: 'UseCached' | 'Refresh'; + + /** + * Origins of issuers from whom to request tokens or redemption + * records. + */ + issuers?: string[]; + } + + export type TrustTokenOperationType = 'Issuance' | 'Redemption' | 'Signing'; + /** * HTTP response data. */ @@ -15107,7 +15450,7 @@ export namespace Cdp { /** * Type of this initiator. */ - type: 'parser' | 'script' | 'preload' | 'SignedExchange' | 'other'; + type: 'parser' | 'script' | 'preload' | 'SignedExchange' | 'preflight' | 'other'; /** * Initiator JavaScript stack trace, set for Script only. @@ -15130,6 +15473,11 @@ export namespace Cdp { * module) (0-based). */ columnNumber?: number; + + /** + * Set if another request triggered this request (e.g. preflight). + */ + requestId?: RequestId; } /** @@ -15207,7 +15555,10 @@ export namespace Cdp { | 'OverwriteSecure' | 'InvalidDomain' | 'InvalidPrefix' - | 'UnknownError'; + | 'UnknownError' + | 'SchemefulSameSiteStrict' + | 'SchemefulSameSiteLax' + | 'SchemefulSameSiteUnspecifiedTreatedAsLax'; /** * Types of reasons why a cookie may not be sent with a request. @@ -15221,7 +15572,10 @@ export namespace Cdp { | 'SameSiteUnspecifiedTreatedAsLax' | 'SameSiteNoneInsecure' | 'UserPreferences' - | 'UnknownError'; + | 'UnknownError' + | 'SchemefulSameSiteStrict' + | 'SchemefulSameSiteLax' + | 'SchemefulSameSiteUnspecifiedTreatedAsLax'; /** * A cookie which was not stored from a response with the corresponding reason. @@ -15531,6 +15885,18 @@ export namespace Cdp { errors?: SignedExchangeError[]; } + export type PrivateNetworkRequestPolicy = 'Allow' | 'BlockFromInsecureToMorePrivate'; + + export type IPAddressSpace = 'Local' | 'Private' | 'Public' | 'Unknown'; + + export interface ClientSecurityState { + initiatorIsSecureContext: boolean; + + initiatorIPAddressSpace: IPAddressSpace; + + privateNetworkRequestPolicy: PrivateNetworkRequestPolicy; + } + export type CrossOriginOpenerPolicyValue = | 'SameOrigin' | 'SameOriginAllowPopups' @@ -15560,9 +15926,9 @@ export namespace Cdp { } export interface SecurityIsolationStatus { - coop: CrossOriginOpenerPolicyStatus; + coop?: CrossOriginOpenerPolicyStatus; - coep: CrossOriginEmbedderPolicyStatus; + coep?: CrossOriginEmbedderPolicyStatus; } /** @@ -15603,89 +15969,406 @@ export namespace Cdp { } /** - * Methods and events of the 'Overlay' domain. + * Methods and events of the 'NodeRuntime' domain. */ - export interface OverlayApi { + export interface NodeRuntimeApi { /** - * Disables domain notifications. + * Enable the `NodeRuntime.waitingForDisconnect`. */ - disable(params: Overlay.DisableParams): Promise; + notifyWhenWaitingForDisconnect( + params: NodeRuntime.NotifyWhenWaitingForDisconnectParams, + ): Promise; /** - * Enables domain notifications. + * This event is fired instead of `Runtime.executionContextDestroyed` when + * enabled. + * It is fired when the Node process finished all code execution and is + * waiting for all frontends to disconnect. */ - enable(params: Overlay.EnableParams): Promise; + on( + event: 'waitingForDisconnect', + listener: (event: NodeRuntime.WaitingForDisconnectEvent) => void, + ): IDisposable; + } + /** + * Types of the 'NodeRuntime' domain. + */ + export namespace NodeRuntime { /** - * For testing. + * Parameters of the 'NodeRuntime.notifyWhenWaitingForDisconnect' method. */ - getHighlightObjectForTest( - params: Overlay.GetHighlightObjectForTestParams, - ): Promise; + export interface NotifyWhenWaitingForDisconnectParams { + enabled: boolean; + } /** - * For Persistent Grid testing. + * Return value of the 'NodeRuntime.notifyWhenWaitingForDisconnect' method. */ - getGridHighlightObjectsForTest( - params: Overlay.GetGridHighlightObjectsForTestParams, - ): Promise; + export interface NotifyWhenWaitingForDisconnectResult {} /** - * For Source Order Viewer testing. + * Parameters of the 'NodeRuntime.waitingForDisconnect' event. */ - getSourceOrderHighlightObjectForTest( - params: Overlay.GetSourceOrderHighlightObjectForTestParams, - ): Promise; + export interface WaitingForDisconnectEvent {} + } + /** + * Methods and events of the 'NodeTracing' domain. + */ + export interface NodeTracingApi { /** - * Hides any highlight. + * Gets supported tracing categories. */ - hideHighlight( - params: Overlay.HideHighlightParams, - ): Promise; + getCategories( + params: NodeTracing.GetCategoriesParams, + ): Promise; /** - * Highlights owner element of the frame with given id. + * Start trace events collection. */ - highlightFrame( - params: Overlay.HighlightFrameParams, - ): Promise; + start(params: NodeTracing.StartParams): Promise; /** - * Highlights DOM node with given id or with the given JavaScript object wrapper. Either nodeId or - * objectId must be specified. + * Stop trace events collection. Remaining collected events will be sent as a sequence of + * dataCollected events followed by tracingComplete event. */ - highlightNode( - params: Overlay.HighlightNodeParams, - ): Promise; + stop(params: NodeTracing.StopParams): Promise; /** - * Highlights given quad. Coordinates are absolute with respect to the main frame viewport. + * Contains an bucket of collected trace events. */ - highlightQuad( - params: Overlay.HighlightQuadParams, - ): Promise; + on( + event: 'dataCollected', + listener: (event: NodeTracing.DataCollectedEvent) => void, + ): IDisposable; /** - * Highlights given rectangle. Coordinates are absolute with respect to the main frame viewport. + * Signals that tracing is stopped and there is no trace buffers pending flush, all data were + * delivered via dataCollected events. */ - highlightRect( - params: Overlay.HighlightRectParams, - ): Promise; + on( + event: 'tracingComplete', + listener: (event: NodeTracing.TracingCompleteEvent) => void, + ): IDisposable; + } + /** + * Types of the 'NodeTracing' domain. + */ + export namespace NodeTracing { /** - * Highlights the source order of the children of the DOM node with given id or with the given - * JavaScript object wrapper. Either nodeId or objectId must be specified. + * Parameters of the 'NodeTracing.getCategories' method. */ - highlightSourceOrder( - params: Overlay.HighlightSourceOrderParams, - ): Promise; + export interface GetCategoriesParams {} /** - * Enters the 'inspect' mode. In this mode, elements that user is hovering over are highlighted. - * Backend then generates 'inspectNodeRequested' event upon element selection. + * Return value of the 'NodeTracing.getCategories' method. */ - setInspectMode( + export interface GetCategoriesResult { + /** + * A list of supported tracing categories. + */ + categories: string[]; + } + + /** + * Parameters of the 'NodeTracing.start' method. + */ + export interface StartParams { + traceConfig: TraceConfig; + } + + /** + * Return value of the 'NodeTracing.start' method. + */ + export interface StartResult {} + + /** + * Parameters of the 'NodeTracing.stop' method. + */ + export interface StopParams {} + + /** + * Return value of the 'NodeTracing.stop' method. + */ + export interface StopResult {} + + /** + * Parameters of the 'NodeTracing.dataCollected' event. + */ + export interface DataCollectedEvent { + value: any[]; + } + + /** + * Parameters of the 'NodeTracing.tracingComplete' event. + */ + export interface TracingCompleteEvent {} + + export interface TraceConfig { + /** + * Controls how the trace buffer stores data. + */ + recordMode?: 'recordUntilFull' | 'recordContinuously' | 'recordAsMuchAsPossible'; + + /** + * Included category filters. + */ + includedCategories: string[]; + } + } + + /** + * Methods and events of the 'NodeWorker' domain. + */ + export interface NodeWorkerApi { + /** + * Sends protocol message over session with given id. + */ + sendMessageToWorker( + params: NodeWorker.SendMessageToWorkerParams, + ): Promise; + + /** + * Instructs the inspector to attach to running workers. Will also attach to new workers + * as they start + */ + enable(params: NodeWorker.EnableParams): Promise; + + /** + * Detaches from all running workers and disables attaching to new workers as they are started. + */ + disable(params: NodeWorker.DisableParams): Promise; + + /** + * Detached from the worker with given sessionId. + */ + detach(params: NodeWorker.DetachParams): Promise; + + /** + * Issued when attached to a worker. + */ + on( + event: 'attachedToWorker', + listener: (event: NodeWorker.AttachedToWorkerEvent) => void, + ): IDisposable; + + /** + * Issued when detached from the worker. + */ + on( + event: 'detachedFromWorker', + listener: (event: NodeWorker.DetachedFromWorkerEvent) => void, + ): IDisposable; + + /** + * Notifies about a new protocol message received from the session + * (session ID is provided in attachedToWorker notification). + */ + on( + event: 'receivedMessageFromWorker', + listener: (event: NodeWorker.ReceivedMessageFromWorkerEvent) => void, + ): IDisposable; + } + + /** + * Types of the 'NodeWorker' domain. + */ + export namespace NodeWorker { + /** + * Parameters of the 'NodeWorker.sendMessageToWorker' method. + */ + export interface SendMessageToWorkerParams { + message: string; + + /** + * Identifier of the session. + */ + sessionId: SessionID; + } + + /** + * Return value of the 'NodeWorker.sendMessageToWorker' method. + */ + export interface SendMessageToWorkerResult {} + + /** + * Parameters of the 'NodeWorker.enable' method. + */ + export interface EnableParams { + /** + * Whether to new workers should be paused until the frontend sends `Runtime.runIfWaitingForDebugger` + * message to run them. + */ + waitForDebuggerOnStart: boolean; + } + + /** + * Return value of the 'NodeWorker.enable' method. + */ + export interface EnableResult {} + + /** + * Parameters of the 'NodeWorker.disable' method. + */ + export interface DisableParams {} + + /** + * Return value of the 'NodeWorker.disable' method. + */ + export interface DisableResult {} + + /** + * Parameters of the 'NodeWorker.detach' method. + */ + export interface DetachParams { + sessionId: SessionID; + } + + /** + * Return value of the 'NodeWorker.detach' method. + */ + export interface DetachResult {} + + /** + * Parameters of the 'NodeWorker.attachedToWorker' event. + */ + export interface AttachedToWorkerEvent { + /** + * Identifier assigned to the session used to send/receive messages. + */ + sessionId: SessionID; + + workerInfo: WorkerInfo; + + waitingForDebugger: boolean; + } + + /** + * Parameters of the 'NodeWorker.detachedFromWorker' event. + */ + export interface DetachedFromWorkerEvent { + /** + * Detached session identifier. + */ + sessionId: SessionID; + } + + /** + * Parameters of the 'NodeWorker.receivedMessageFromWorker' event. + */ + export interface ReceivedMessageFromWorkerEvent { + /** + * Identifier of a session which sends a message. + */ + sessionId: SessionID; + + message: string; + } + + export type WorkerID = string; + + /** + * Unique identifier of attached debugging session. + */ + export type SessionID = string; + + export interface WorkerInfo { + workerId: WorkerID; + + type: string; + + title: string; + + url: string; + } + } + + /** + * Methods and events of the 'Overlay' domain. + */ + export interface OverlayApi { + /** + * Disables domain notifications. + */ + disable(params: Overlay.DisableParams): Promise; + + /** + * Enables domain notifications. + */ + enable(params: Overlay.EnableParams): Promise; + + /** + * For testing. + */ + getHighlightObjectForTest( + params: Overlay.GetHighlightObjectForTestParams, + ): Promise; + + /** + * For Persistent Grid testing. + */ + getGridHighlightObjectsForTest( + params: Overlay.GetGridHighlightObjectsForTestParams, + ): Promise; + + /** + * For Source Order Viewer testing. + */ + getSourceOrderHighlightObjectForTest( + params: Overlay.GetSourceOrderHighlightObjectForTestParams, + ): Promise; + + /** + * Hides any highlight. + */ + hideHighlight( + params: Overlay.HideHighlightParams, + ): Promise; + + /** + * Highlights owner element of the frame with given id. + */ + highlightFrame( + params: Overlay.HighlightFrameParams, + ): Promise; + + /** + * Highlights DOM node with given id or with the given JavaScript object wrapper. Either nodeId or + * objectId must be specified. + */ + highlightNode( + params: Overlay.HighlightNodeParams, + ): Promise; + + /** + * Highlights given quad. Coordinates are absolute with respect to the main frame viewport. + */ + highlightQuad( + params: Overlay.HighlightQuadParams, + ): Promise; + + /** + * Highlights given rectangle. Coordinates are absolute with respect to the main frame viewport. + */ + highlightRect( + params: Overlay.HighlightRectParams, + ): Promise; + + /** + * Highlights the source order of the children of the DOM node with given id or with the given + * JavaScript object wrapper. Either nodeId or objectId must be specified. + */ + highlightSourceOrder( + params: Overlay.HighlightSourceOrderParams, + ): Promise; + + /** + * Enters the 'inspect' mode. In this mode, elements that user is hovering over are highlighted. + * Backend then generates 'inspectNodeRequested' event upon element selection. + */ + setInspectMode( params: Overlay.SetInspectModeParams, ): Promise; @@ -15721,6 +16404,10 @@ export namespace Cdp { params: Overlay.SetShowGridOverlaysParams, ): Promise; + setShowFlexOverlays( + params: Overlay.SetShowFlexOverlaysParams, + ): Promise; + /** * Requests that backend shows paint rectangles */ @@ -16162,6 +16849,21 @@ export namespace Cdp { */ export interface SetShowGridOverlaysResult {} + /** + * Parameters of the 'Overlay.setShowFlexOverlays' method. + */ + export interface SetShowFlexOverlaysParams { + /** + * An array of node identifiers and descriptors for the highlight appearance. + */ + flexNodeHighlightConfigs: FlexNodeHighlightConfig[]; + } + + /** + * Return value of the 'Overlay.setShowFlexOverlays' method. + */ + export interface SetShowFlexOverlaysResult {} + /** * Parameters of the 'Overlay.setShowPaintRects' method. */ @@ -16406,6 +17108,83 @@ export namespace Cdp { gridBackgroundColor?: DOM.RGBA; } + /** + * Configuration data for the highlighting of Flex container elements. + */ + export interface FlexContainerHighlightConfig { + /** + * The style of the container border + */ + containerBorder?: LineStyle; + + /** + * The style of the separator between lines + */ + lineSeparator?: LineStyle; + + /** + * The style of the separator between items + */ + itemSeparator?: LineStyle; + + /** + * Style of content-distribution space on the main axis (justify-content). + */ + mainDistributedSpace?: BoxStyle; + + /** + * Style of content-distribution space on the cross axis (align-content). + */ + crossDistributedSpace?: BoxStyle; + + /** + * Style of empty space caused by row gaps (gap/row-gap). + */ + rowGapSpace?: BoxStyle; + + /** + * Style of empty space caused by columns gaps (gap/column-gap). + */ + columnGapSpace?: BoxStyle; + + /** + * Style of the self-alignment line (align-items). + */ + crossAlignment?: LineStyle; + } + + /** + * Style information for drawing a line. + */ + export interface LineStyle { + /** + * The color of the line (default: transparent) + */ + color?: DOM.RGBA; + + /** + * The line pattern (default: solid) + */ + pattern?: 'dashed' | 'dotted'; + } + + /** + * Style information for drawing a box. + */ + export interface BoxStyle { + /** + * The background color for the box (default: transparent) + */ + fillColor?: DOM.RGBA; + + /** + * The hatching color for the box (default: transparent) + */ + hatchColor?: DOM.RGBA; + } + + export type ContrastAlgorithm = 'aa' | 'aaa' | 'apca'; + /** * Configuration data for the highlighting of page elements. */ @@ -16484,6 +17263,16 @@ export namespace Cdp { * The grid layout highlight configuration (default: all transparent). */ gridHighlightConfig?: GridHighlightConfig; + + /** + * The flex container highlight configuration (default: all transparent). + */ + flexContainerHighlightConfig?: FlexContainerHighlightConfig; + + /** + * The contrast algorithm to use for the contrast ratio (default: aa). + */ + contrastAlgorithm?: ContrastAlgorithm; } export type ColorFormat = 'rgb' | 'hsl' | 'hex'; @@ -16503,6 +17292,18 @@ export namespace Cdp { nodeId: DOM.NodeId; } + export interface FlexNodeHighlightConfig { + /** + * A descriptor for the highlight appearance of flex containers. + */ + flexContainerHighlightConfig: FlexContainerHighlightConfig; + + /** + * Identifier of the node to highlight. + */ + nodeId: DOM.NodeId; + } + /** * Configuration for dual screen hinge */ @@ -16935,6 +17736,11 @@ export namespace Cdp { */ on(event: 'frameNavigated', listener: (event: Page.FrameNavigatedEvent) => void): IDisposable; + /** + * Fired when opening document to write to. + */ + on(event: 'documentOpened', listener: (event: Page.DocumentOpenedEvent) => void): IDisposable; + on(event: 'frameResized', listener: (event: Page.FrameResizedEvent) => void): IDisposable; /** @@ -17143,6 +17949,11 @@ export namespace Cdp { * Capture the screenshot from the surface, rather than the view. Defaults to true. */ fromSurface?: boolean; + + /** + * Capture the screenshot beyond the viewport. Defaults to false. + */ + captureBeyondViewport?: boolean; } /** @@ -18247,6 +19058,8 @@ export namespace Cdp { * Id of the frame that has been detached. */ frameId: FrameId; + + reason: 'remove' | 'swap'; } /** @@ -18259,6 +19072,16 @@ export namespace Cdp { frame: Frame; } + /** + * Parameters of the 'Page.documentOpened' event. + */ + export interface DocumentOpenedEvent { + /** + * Frame object. + */ + frame: Frame; + } + /** * Parameters of the 'Page.frameResized' event. */ @@ -18577,6 +19400,12 @@ export namespace Cdp { | 'NotIsolated' | 'NotIsolatedFeatureDisabled'; + export type GatedAPIFeatures = + | 'SharedArrayBuffers' + | 'SharedArrayBuffersTransferAllowed' + | 'PerformanceMeasureMemory' + | 'PerformanceProfile'; + /** * Information about the Frame on the page. */ @@ -18648,6 +19477,11 @@ export namespace Cdp { * Indicates whether this is a cross origin isolated context. */ crossOriginIsolatedContextType: CrossOriginIsolatedContextType; + + /** + * Indicated which gated APIs / features are available. + */ + gatedAPIFeatures: GatedAPIFeatures[]; } /** @@ -22165,6 +22999,13 @@ export namespace Cdp { params: Storage.GetUsageAndQuotaParams, ): Promise; + /** + * Override quota for the specified origin + */ + overrideQuotaForOrigin( + params: Storage.OverrideQuotaForOriginParams, + ): Promise; + /** * Registers origin to be notified when an update occurs to its cache storage list. */ @@ -22329,12 +23170,43 @@ export namespace Cdp { */ quota: number; + /** + * Whether or not the origin has an active storage quota override + */ + overrideActive: boolean; + /** * Storage usage per type (bytes). */ usageBreakdown: UsageForType[]; } + /** + * Parameters of the 'Storage.overrideQuotaForOrigin' method. + */ + export interface OverrideQuotaForOriginParams { + /** + * Security origin. + */ + origin: string; + + /** + * The quota size (in bytes) to override the original quota with. + * If this is called multiple times, the overriden quota will be equal to + * the quotaSize provided in the final call. If this is called without + * specifying a quotaSize, the quota will be reset to the default value for + * the specified origin. If this is called multiple times with different + * origins, the override will be maintained for each origin until it is + * disabled (called without a quotaSize). + */ + quotaSize?: number; + } + + /** + * Return value of the 'Storage.overrideQuotaForOrigin' method. + */ + export interface OverrideQuotaForOriginResult {} + /** * Parameters of the 'Storage.trackCacheStorageForOrigin' method. */ @@ -23572,6 +24444,11 @@ export namespace Cdp { * Enables more deterministic results by forcing garbage collection */ deterministic?: boolean; + + /** + * Specifies level of details in memory dump. Defaults to "detailed". + */ + levelOfDetail?: MemoryDumpLevelOfDetail; } /** @@ -23754,6 +24631,13 @@ export namespace Cdp { * Compression type to use for traces returned via streams. */ export type StreamCompression = 'none' | 'gzip'; + + /** + * Details exposed when memory request explicitly declared. + * Keep consistent with memory_dump_request_args.h and + * memory_instrumentation.mojom + */ + export type MemoryDumpLevelOfDetail = 'background' | 'light' | 'detailed'; } /** @@ -24428,11 +25312,18 @@ export namespace Cdp { export type AuthenticatorProtocol = 'u2f' | 'ctap2'; + export type Ctap2Version = 'ctap2_0' | 'ctap2_1'; + export type AuthenticatorTransport = 'usb' | 'nfc' | 'ble' | 'cable' | 'internal'; export interface VirtualAuthenticatorOptions { protocol: AuthenticatorProtocol; + /** + * Defaults to ctap2_0. Ignored if |protocol| == u2f. + */ + ctap2Version?: Ctap2Version; + transport: AuthenticatorTransport; /** @@ -24493,6 +25384,12 @@ export namespace Cdp { * See https://w3c.github.io/webauthn/#signature-counter */ signCount: integer; + + /** + * The large blob associated with the credential. + * See https://w3c.github.io/webauthn/#sctn-large-blob-extension + */ + largeBlob?: string; } } } diff --git a/src/cdp/telemetryClassification.d.ts b/src/cdp/telemetryClassification.d.ts index 2d6d9487b..4b2e34a45 100644 --- a/src/cdp/telemetryClassification.d.ts +++ b/src/cdp/telemetryClassification.d.ts @@ -219,10 +219,34 @@ interface ICDPOperationClassification { '!network.websockethandshakeresponsereceived.errors': { classification: 'CallstackOrException'; purpose: 'PerformanceAndHealth' }; 'network.websocketwillsendhandshakerequest': { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth' }; '!network.websocketwillsendhandshakerequest.errors': { classification: 'CallstackOrException'; purpose: 'PerformanceAndHealth' }; + 'network.webtransportcreated': { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth' }; + '!network.webtransportcreated.errors': { classification: 'CallstackOrException'; purpose: 'PerformanceAndHealth' }; + 'network.webtransportclosed': { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth' }; + '!network.webtransportclosed.errors': { classification: 'CallstackOrException'; purpose: 'PerformanceAndHealth' }; 'network.requestwillbesentextrainfo': { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth' }; '!network.requestwillbesentextrainfo.errors': { classification: 'CallstackOrException'; purpose: 'PerformanceAndHealth' }; 'network.responsereceivedextrainfo': { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth' }; '!network.responsereceivedextrainfo.errors': { classification: 'CallstackOrException'; purpose: 'PerformanceAndHealth' }; + 'network.trusttokenoperationdone': { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth' }; + '!network.trusttokenoperationdone.errors': { classification: 'CallstackOrException'; purpose: 'PerformanceAndHealth' }; + + // Domain: NodeRuntime + 'noderuntime.waitingfordisconnect': { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth' }; + '!noderuntime.waitingfordisconnect.errors': { classification: 'CallstackOrException'; purpose: 'PerformanceAndHealth' }; + + // Domain: NodeTracing + 'nodetracing.datacollected': { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth' }; + '!nodetracing.datacollected.errors': { classification: 'CallstackOrException'; purpose: 'PerformanceAndHealth' }; + 'nodetracing.tracingcomplete': { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth' }; + '!nodetracing.tracingcomplete.errors': { classification: 'CallstackOrException'; purpose: 'PerformanceAndHealth' }; + + // Domain: NodeWorker + 'nodeworker.attachedtoworker': { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth' }; + '!nodeworker.attachedtoworker.errors': { classification: 'CallstackOrException'; purpose: 'PerformanceAndHealth' }; + 'nodeworker.detachedfromworker': { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth' }; + '!nodeworker.detachedfromworker.errors': { classification: 'CallstackOrException'; purpose: 'PerformanceAndHealth' }; + 'nodeworker.receivedmessagefromworker': { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth' }; + '!nodeworker.receivedmessagefromworker.errors': { classification: 'CallstackOrException'; purpose: 'PerformanceAndHealth' }; // Domain: Overlay 'overlay.inspectnoderequested': { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth' }; @@ -247,6 +271,8 @@ interface ICDPOperationClassification { '!page.framedetached.errors': { classification: 'CallstackOrException'; purpose: 'PerformanceAndHealth' }; 'page.framenavigated': { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth' }; '!page.framenavigated.errors': { classification: 'CallstackOrException'; purpose: 'PerformanceAndHealth' }; + 'page.documentopened': { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth' }; + '!page.documentopened.errors': { classification: 'CallstackOrException'; purpose: 'PerformanceAndHealth' }; 'page.frameresized': { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth' }; '!page.frameresized.errors': { classification: 'CallstackOrException'; purpose: 'PerformanceAndHealth' }; 'page.framerequestednavigation': { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth' }; diff --git a/src/cdp/workerTransport.ts b/src/cdp/workerTransport.ts new file mode 100644 index 000000000..99ea0be2c --- /dev/null +++ b/src/cdp/workerTransport.ts @@ -0,0 +1,48 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { DisposableList } from '../common/disposable'; +import { EventEmitter } from '../common/events'; +import { HrTime } from '../common/hrnow'; +import Cdp from './api'; +import { ITransport } from './transport'; + +/** + * Transport used for debugging node worker threads over the NodeTarget API. + */ +export class WorkerTransport implements ITransport { + private readonly onMessageEmitter = new EventEmitter<[string, HrTime]>(); + private readonly onEndEmitter = new EventEmitter(); + private readonly disposables = new DisposableList(); + + public readonly onMessage = this.onMessageEmitter.event; + public readonly onEnd = this.onEndEmitter.event; + + constructor(private readonly sessionId: string, private readonly sink: Cdp.Api) { + this.disposables.push( + sink.NodeWorker.on('detachedFromWorker', evt => { + if (evt.sessionId === sessionId) { + this.onEndEmitter.fire(); + this.dispose(); + } + }), + sink.NodeWorker.on('receivedMessageFromWorker', evt => { + if (evt.sessionId === sessionId) { + this.onMessageEmitter.fire([evt.message, new HrTime()]); + } + }), + ); + } + + send(message: string): void { + this.sink.NodeWorker.sendMessageToWorker({ message, sessionId: this.sessionId }); + } + + dispose(): void { + if (!this.disposables.isDisposed) { + this.disposables.dispose(); + this.onEndEmitter.fire(); + } + } +} diff --git a/src/common/disposable.ts b/src/common/disposable.ts index fbfdeeeb3..94a5af39a 100644 --- a/src/common/disposable.ts +++ b/src/common/disposable.ts @@ -20,6 +20,10 @@ export class DisposableList { private disposed = false; private items: IDisposable[] = []; + public get isDisposed() { + return this.disposed; + } + constructor(initialItems?: ReadonlyArray) { if (initialItems) { this.items = initialItems.slice(); diff --git a/src/targets/browser/browserTargets.ts b/src/targets/browser/browserTargets.ts index 61719492f..3624cf396 100644 --- a/src/targets/browser/browserTargets.ts +++ b/src/targets/browser/browserTargets.ts @@ -233,12 +233,6 @@ export class BrowserTarget implements ITarget, IThreadDelegate { return domDebuggerTypes.has(this.type()); } - shouldCheckContentHash(): boolean { - // Browser executes scripts retrieved from network. - // We check content hash because served code can be different from actual files on disk. - return true; - } - scriptUrlToUrl(url: string): string { return urlUtils.completeUrl(this._targetInfo.url, url) || url; } diff --git a/src/targets/node/nodeLauncherBase.ts b/src/targets/node/nodeLauncherBase.ts index c9ebd617a..86eaf86aa 100644 --- a/src/targets/node/nodeLauncherBase.ts +++ b/src/targets/node/nodeLauncherBase.ts @@ -11,9 +11,11 @@ import { getSourceSuffix } from '../../adapter/templates'; import Cdp from '../../cdp/api'; import Connection from '../../cdp/connection'; import { RawPipeTransport } from '../../cdp/rawPipeTransport'; +import { WorkerTransport } from '../../cdp/workerTransport'; import { CancellationTokenSource } from '../../common/cancellation'; import { AutoAttachMode } from '../../common/contributionUtils'; import { ObservableMap } from '../../common/datastructure/observableMap'; +import { DisposableList } from '../../common/disposable'; import { EnvironmentVars } from '../../common/environmentVars'; import { EventEmitter } from '../../common/events'; import { IFsUtils } from '../../common/fsUtils'; @@ -42,6 +44,7 @@ import { } from './nodeBinaryProvider'; import { NodeSourcePathResolver } from './nodeSourcePathResolver'; import { INodeTargetLifecycleHooks, NodeTarget } from './nodeTarget'; +import { NodeWorkerTarget } from './nodeWorkerTarget'; import { IProgram } from './program'; /** @@ -98,7 +101,7 @@ export abstract class NodeLauncherBase implement /** * Target list. */ - private readonly targets = new ObservableMap(); + private readonly targets = new ObservableMap(); /** * Underlying emitter fired when sessions terminate. Listened to by the @@ -401,17 +404,51 @@ export abstract class NodeLauncherBase implement targetInfo, this.run.logger, this.createLifecycle(cdp, this.run, targetInfo), + targetInfo.openerId ? this.targets.get(targetInfo.openerId) : undefined, ); - target.setParent(targetInfo.openerId ? this.targets.get(targetInfo.openerId) : undefined); + this.listenToWorkerDomain(cdp, telemetryReporter, target); this.targets.add(targetInfo.targetId, target); target.onDisconnect(() => this.targets.remove(targetInfo.targetId)); } + private async listenToWorkerDomain( + cdp: Cdp.Api, + telemetryReporter: ITelemetryReporter, + parent: NodeTarget, + ) { + cdp.NodeWorker.on('attachedToWorker', evt => { + const transport = new WorkerTransport(evt.sessionId, cdp); + const target = new NodeWorkerTarget( + parent.launchConfig, + { + attached: true, + canAccessOpener: false, + type: 'node-worker', + targetId: evt.sessionId, + title: evt.workerInfo.title, + url: evt.workerInfo.url, + openerId: parent.id(), + }, + parent, + parent.targetOrigin(), + new Connection(transport, parent.logger, telemetryReporter).rootSession(), + parent.sourcePathResolver(), + parent.logger, + ); + + const disposables = new DisposableList(); + disposables.push(transport); + disposables.push(parent.onDisconnect(() => disposables.dispose())); + disposables.push(transport.onEnd(() => disposables.dispose())); + disposables.callback(() => this.targets.remove(target.id())); + this.targets.add(target.id(), target); + }); + } + /** * Acquires the CDP session and target info from the connecting socket. */ - protected async acquireTarget( socket: net.Socket, rawTelemetryReporter: ITelemetryReporter, diff --git a/src/targets/node/nodeTarget.ts b/src/targets/node/nodeTarget.ts index a410141c6..ceceb894e 100644 --- a/src/targets/node/nodeTarget.ts +++ b/src/targets/node/nodeTarget.ts @@ -30,11 +30,7 @@ export interface INodeTargetLifecycleHooks { export class NodeTarget implements ITarget, IThreadDelegate { private _cdp: Cdp.Api; - private _parent: NodeTarget | undefined; - private _children: NodeTarget[] = []; - private _targetId: string; private _targetName: string; - private _scriptName: string; private _serialize: Promise = Promise.resolve(undefined); private _attached = false; private _waitingForDebugger: boolean; @@ -56,12 +52,11 @@ export class NodeTarget implements ITarget, IThreadDelegate { public readonly targetInfo: Cdp.Target.TargetInfo, public readonly logger: ILogger, private readonly lifecycle: INodeTargetLifecycleHooks = {}, + private readonly _parent: ITarget | undefined, ) { this.connection = connection; this._cdp = cdp; cdp.pause(); - this._targetId = targetInfo.targetId; - this._scriptName = targetInfo.title; this._waitingForDebugger = targetInfo.type === 'waitingForDebugger'; if (targetInfo.title) this._targetName = `${basename(targetInfo.title)} [${targetInfo.targetId}]`; @@ -72,7 +67,7 @@ export class NodeTarget implements ITarget, IThreadDelegate { } id(): string { - return this._targetId; + return this.targetInfo.targetId; } name(): string { @@ -80,7 +75,7 @@ export class NodeTarget implements ITarget, IThreadDelegate { } fileName(): string | undefined { - return this._scriptName; + return this.targetInfo.title; } type(): string { @@ -95,10 +90,6 @@ export class NodeTarget implements ITarget, IThreadDelegate { return this._parent; } - children(): ITarget[] { - return Array.from(this._children.values()); - } - public async initialize() { if (this.lifecycle.initialized) { this.entryBreakpoint = (await this.lifecycle.initialized(this)) || undefined; @@ -123,11 +114,6 @@ export class NodeTarget implements ITarget, IThreadDelegate { return false; } - shouldCheckContentHash(): boolean { - // todo(connor4312): all targets need content hashing, remove dead code - return true; - } - executionContextName(): string { return this._targetName; } @@ -140,15 +126,7 @@ export class NodeTarget implements ITarget, IThreadDelegate { await this._cdp.Runtime.runIfWaitingForDebugger({}); } - public setParent(parent?: NodeTarget) { - if (this._parent) this._parent._children.splice(this._parent._children.indexOf(this), 1); - this._parent = parent; - if (this._parent) this._parent._children.push(this); - } - private async _disconnected() { - this._children.forEach(child => child.setParent(this._parent)); - this.setParent(undefined); this._onDisconnectEmitter.fire(); } @@ -167,14 +145,16 @@ export class NodeTarget implements ITarget, IThreadDelegate { async _doAttach(): Promise { this._waitingForDebugger = false; this._attached = true; - const result = await this._cdp.Target.attachToTarget({ targetId: this._targetId }); + const result = await this._cdp.Target.attachToTarget({ targetId: this.targetInfo.targetId }); if (!result) { this.logger.info(LogTag.RuntimeLaunch, 'Failed to attach to target', { - targetId: this._targetId, + targetId: this.targetInfo.targetId, }); return; // timed out or cancelled, may have been a short-lived process } + this._cdp.NodeWorker.enable({ waitForDebuggerOnStart: true }); + if (result && '__dynamicAttach' in result) { await this._cdp.Debugger.enable({}); await this._cdp.Runtime.enable({}); @@ -215,7 +195,11 @@ export class NodeTarget implements ITarget, IThreadDelegate { } async _doDetach() { - await this._cdp.Target.detachFromTarget({ targetId: this._targetId }); + await Promise.all([ + this._cdp.Target.detachFromTarget({ targetId: this.targetInfo.targetId }), + this._cdp.NodeWorker.disable({}), + ]); + this.connection.close(); this._attached = false; } diff --git a/src/targets/node/nodeWorkerTarget.ts b/src/targets/node/nodeWorkerTarget.ts new file mode 100644 index 000000000..7a7b193e1 --- /dev/null +++ b/src/targets/node/nodeWorkerTarget.ts @@ -0,0 +1,133 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { IThreadDelegate } from '../../adapter/threads'; +import Cdp from '../../cdp/api'; +import { EventEmitter } from '../../common/events'; +import { ILogger } from '../../common/logging'; +import { ISourcePathResolver } from '../../common/sourcePathResolver'; +import { absolutePathToFileUrl } from '../../common/urlUtils'; +import { AnyLaunchConfiguration } from '../../configuration'; +import { ITargetOrigin } from '../targetOrigin'; +import { ITarget } from '../targets'; +import { NodeTarget } from './nodeTarget'; + +export class NodeWorkerTarget implements ITarget, IThreadDelegate { + public readonly onNameChanged = new EventEmitter().event; + private attached = false; + private isWaitingForDebugger = true; + + constructor( + public readonly launchConfig: AnyLaunchConfiguration, + public readonly targetInfo: Cdp.Target.TargetInfo, + private readonly parentTarget: NodeTarget, + private readonly targetOriginValue: ITargetOrigin, + private readonly cdp: Cdp.Api, + private readonly pathResolver: ISourcePathResolver, + public readonly logger: ILogger, + ) { + cdp.pause(); + } + + id(): string { + return this.targetInfo.targetId; + } + + name(): string { + return this.targetInfo.title; + } + + fileName(): string | undefined { + return this.targetInfo.url; + } + + type(): string { + return 'node'; + } + + parent(): ITarget | undefined { + return this.parentTarget; + } + + children(): ITarget[] { + return []; + } + + canStop(): boolean { + return false; + } + + stop(): void { + // no-op + } + + canRestart(): boolean { + return false; + } + restart(): void { + // no-op + } + + canAttach(): boolean { + return !this.attached; + } + + public async attach(): Promise { + await Promise.all([this.cdp.Debugger.enable({}), this.cdp.Runtime.enable({})]); + this.attached = true; + return this.cdp; + } + + public canDetach(): boolean { + return this.attached; + } + + public async detach(): Promise { + // there seems to be a bug where if we detach while paused, the worker will remain paused + await this.cdp.Debugger.resume({}); + await this.cdp.NodeWorker.detach({ sessionId: this.targetInfo.targetId }); + this.attached = false; + } + + public targetOrigin(): ITargetOrigin { + return this.targetOriginValue; + } + + public afterBind(): Promise { + this.cdp.resume(); + return Promise.resolve(); + } + + public async runIfWaitingForDebugger(): Promise { + this.isWaitingForDebugger = false; + await this.cdp.Runtime.runIfWaitingForDebugger({}); + } + + public initialize(): Promise { + return Promise.resolve(); + } + + public waitingForDebugger(): boolean { + return this.isWaitingForDebugger; + } + + supportsCustomBreakpoints(): boolean { + return false; + } + + scriptUrlToUrl(url: string): string { + // copied from NodeTarget. Todo: should be merged into the path resolver logic + const isPath = + url[0] === '/' || (process.platform === 'win32' && url[1] === ':' && url[2] === '\\'); + return isPath ? absolutePathToFileUrl(url) : url; + } + + sourcePathResolver(): ISourcePathResolver { + return this.pathResolver; + } + + executionContextName(): string { + return this.targetInfo.title; + } +} diff --git a/src/targets/targets.ts b/src/targets/targets.ts index 8b4c13cfd..8ced33370 100644 --- a/src/targets/targets.ts +++ b/src/targets/targets.ts @@ -44,7 +44,6 @@ export interface ITarget { fileName(): string | undefined; type(): string; parent(): ITarget | undefined; - children(): ITarget[]; canStop(): boolean; stop(): void; canRestart(): boolean; @@ -69,11 +68,10 @@ export interface ITarget { initialize(): Promise; waitingForDebugger(): boolean; supportsCustomBreakpoints(): boolean; - shouldCheckContentHash(): boolean; scriptUrlToUrl(url: string): string; sourcePathResolver(): ISourcePathResolver; executionContextName(context: Cdp.Runtime.ExecutionContextDescription): string; - entryBreakpoint: IBreakpointPathAndId | undefined; + entryBreakpoint?: IBreakpointPathAndId | undefined; logger: ILogger; } diff --git a/src/test/browser/framesTest.ts b/src/test/browser/framesTest.ts index 665437c3b..3783c29fa 100644 --- a/src/test/browser/framesTest.ts +++ b/src/test/browser/framesTest.ts @@ -2,7 +2,7 @@ * Copyright (C) Microsoft Corporation. All rights reserved. *--------------------------------------------------------*/ -import { ITarget } from '../../targets/targets'; +import { BrowserTarget } from '../../targets/browser/browserTargets'; import { itIntegrates } from '../testIntegrationUtils'; describe('frames', () => { @@ -11,7 +11,7 @@ describe('frames', () => { const p = await r.launchUrl('frames.html'); p.load(); - const logTarget = (t: ITarget, indent: number) => { + const logTarget = (t: BrowserTarget, indent: number) => { const s = ' '.repeat(indent); p.log( `${s}${t.type()} "${t.name()}" [thread "${t.scriptUrlToUrl('')}"]${ @@ -22,10 +22,10 @@ describe('frames', () => { children.sort((t1, t2) => { return t1.name().localeCompare(t2.name()); }); - children.forEach(child => logTarget(child, indent + 2)); + children.forEach(child => logTarget(child as BrowserTarget, indent + 2)); }; - await new Promise(f => { + await new Promise(f => { r.onSessionCreated(() => { if (r.binder.targetList().length === 11) f(); }); @@ -33,7 +33,7 @@ describe('frames', () => { r.binder .targetList() .filter(t => !t.parent()) - .forEach(target => logTarget(target, 0)); + .forEach(target => logTarget(target as BrowserTarget, 0)); p.assertLog(); }); diff --git a/src/test/node/node-runtime-debugs-worker-threads.txt b/src/test/node/node-runtime-debugs-worker-threads.txt new file mode 100644 index 000000000..35e6c862a --- /dev/null +++ b/src/test/node/node-runtime-debugs-worker-threads.txt @@ -0,0 +1,7 @@ +{ + allThreadsStopped : false + description : Paused on breakpoint + reason : breakpoint + threadId : +} + @ ${fixturesDir}/test.js:6:5 diff --git a/src/test/node/node-runtime.test.ts b/src/test/node/node-runtime.test.ts index 42538ed8c..870cfc7ee 100644 --- a/src/test/node/node-runtime.test.ts +++ b/src/test/node/node-runtime.test.ts @@ -117,6 +117,38 @@ describe('node runtime', () => { await handle.dap.once('terminated'); }); + if (process.env.ONLY_MINSPEC !== 'true') { + // not available on node 8 + itIntegrates('debugs worker threads', async ({ r }) => { + createFileTree(testFixturesDir, { + 'test.js': [ + 'const { Worker, isMainThread, workerData } = require("worker_threads");', + 'if (isMainThread) {', + ' new Worker(__filename, { workerData: { greet: "world" } });', + '} else {', + ' setInterval(() => {', + ' console.log("hello " + workerData.greet);', + ' }, 100);', + '}', + ], + }); + + const handle = await r.runScript('test.js'); + handle.load(); + + const worker = await r.worker(); + await worker.dap.setBreakpoints({ + source: { path: join(testFixturesDir, 'test.js') }, + breakpoints: [{ line: 6, column: 1 }], + }); + + worker.load(); + + await waitForPause(worker); + handle.assertLog({ substring: true }); + }); + } + itIntegrates('exits with integrated terminal launcher', async ({ r }) => { // We don't actually attach the DAP fully through vscode, so stub about // the launch request. We just want to test that the lifecycle of a detached