devtools-tracing 1.0.1 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. package/generate.ts +32 -26
  2. package/index.ts +2 -1
  3. package/lib/extension-api/ExtensionAPI.d.ts +357 -0
  4. package/lib/front_end/models/bindings/CSSWorkspaceBinding.ts +318 -0
  5. package/lib/front_end/models/bindings/CompilerScriptMapping.ts +536 -0
  6. package/lib/front_end/models/bindings/ContentProviderBasedProject.ts +187 -0
  7. package/lib/front_end/models/bindings/DebuggerLanguagePlugins.ts +1197 -0
  8. package/lib/front_end/models/bindings/DebuggerWorkspaceBinding.ts +733 -0
  9. package/lib/front_end/models/bindings/DefaultScriptMapping.ts +141 -0
  10. package/lib/front_end/models/bindings/FileUtils.ts +228 -0
  11. package/lib/front_end/models/bindings/LiveLocation.ts +81 -0
  12. package/lib/front_end/models/bindings/NetworkProject.ts +157 -0
  13. package/lib/front_end/models/bindings/PresentationConsoleMessageHelper.ts +312 -0
  14. package/lib/front_end/models/bindings/ResourceMapping.ts +539 -0
  15. package/lib/front_end/models/bindings/ResourceScriptMapping.ts +491 -0
  16. package/lib/front_end/models/bindings/ResourceUtils.ts +103 -0
  17. package/lib/front_end/models/bindings/SASSSourceMapping.ts +222 -0
  18. package/lib/front_end/models/bindings/StylesSourceMapping.ts +316 -0
  19. package/lib/front_end/models/bindings/TempFile.ts +67 -0
  20. package/lib/front_end/models/bindings/bindings.ts +39 -0
  21. package/lib/front_end/models/source_map_scopes/NamesResolver.ts +765 -0
  22. package/lib/front_end/models/source_map_scopes/ScopeChainModel.ts +84 -0
  23. package/lib/front_end/models/source_map_scopes/source_map_scopes.ts +11 -0
  24. package/lib/front_end/models/stack_trace/StackTrace.ts +53 -0
  25. package/lib/front_end/models/stack_trace/StackTraceImpl.ts +85 -0
  26. package/lib/front_end/models/stack_trace/StackTraceModel.ts +128 -0
  27. package/lib/front_end/models/stack_trace/Trie.ts +163 -0
  28. package/lib/front_end/models/stack_trace/stack_trace.ts +9 -0
  29. package/lib/front_end/models/stack_trace/stack_trace_impl.ts +13 -0
  30. package/lib/front_end/models/trace_source_maps_resolver/SourceMapsResolver.ts +240 -0
  31. package/lib/front_end/models/trace_source_maps_resolver/trace_source_maps_resolver.ts +5 -0
  32. package/lib/front_end/models/workspace/FileManager.ts +97 -0
  33. package/lib/front_end/models/workspace/IgnoreListManager.ts +628 -0
  34. package/lib/front_end/models/workspace/SearchConfig.ts +149 -0
  35. package/lib/front_end/models/workspace/UISourceCode.ts +698 -0
  36. package/lib/front_end/models/workspace/WorkspaceImpl.ts +339 -0
  37. package/lib/front_end/models/workspace/workspace.ts +17 -0
  38. package/lib/front_end/panels/timeline/TimelineUIUtils.ts +1029 -0
  39. package/lib/front_end/panels/timeline/extensions/ExtensionUI.ts +49 -0
  40. package/lib/front_end/panels/timeline/extensions/extensions.ts +9 -0
  41. package/lib/front_end/third_party/codemirror.next/LICENSE +21 -0
  42. package/lib/front_end/third_party/codemirror.next/README.chromium +30 -0
  43. package/lib/front_end/third_party/codemirror.next/bundle-tsconfig.json +24 -0
  44. package/lib/front_end/third_party/codemirror.next/bundle.ts +135 -0
  45. package/lib/front_end/third_party/codemirror.next/chunk/angular.js +2 -0
  46. package/lib/front_end/third_party/codemirror.next/chunk/angular.js.map +1 -0
  47. package/lib/front_end/third_party/codemirror.next/chunk/codemirror.js +2 -0
  48. package/lib/front_end/third_party/codemirror.next/chunk/codemirror.js.map +1 -0
  49. package/lib/front_end/third_party/codemirror.next/chunk/cpp.js +2 -0
  50. package/lib/front_end/third_party/codemirror.next/chunk/cpp.js.map +1 -0
  51. package/lib/front_end/third_party/codemirror.next/chunk/css.js +2 -0
  52. package/lib/front_end/third_party/codemirror.next/chunk/html.js +4 -0
  53. package/lib/front_end/third_party/codemirror.next/chunk/java.js +2 -0
  54. package/lib/front_end/third_party/codemirror.next/chunk/java.js.map +1 -0
  55. package/lib/front_end/third_party/codemirror.next/chunk/javascript.js +2 -0
  56. package/lib/front_end/third_party/codemirror.next/chunk/legacy.js +2 -0
  57. package/lib/front_end/third_party/codemirror.next/chunk/legacy.js.map +1 -0
  58. package/lib/front_end/third_party/codemirror.next/chunk/less.js +2 -0
  59. package/lib/front_end/third_party/codemirror.next/chunk/less.js.map +1 -0
  60. package/lib/front_end/third_party/codemirror.next/chunk/markdown.js +2 -0
  61. package/lib/front_end/third_party/codemirror.next/chunk/markdown.js.map +1 -0
  62. package/lib/front_end/third_party/codemirror.next/chunk/php.js +2 -0
  63. package/lib/front_end/third_party/codemirror.next/chunk/php.js.map +1 -0
  64. package/lib/front_end/third_party/codemirror.next/chunk/python.js +2 -0
  65. package/lib/front_end/third_party/codemirror.next/chunk/python.js.map +1 -0
  66. package/lib/front_end/third_party/codemirror.next/chunk/sass.js +2 -0
  67. package/lib/front_end/third_party/codemirror.next/chunk/sass.js.map +1 -0
  68. package/lib/front_end/third_party/codemirror.next/chunk/svelte.js +2 -0
  69. package/lib/front_end/third_party/codemirror.next/chunk/svelte.js.map +1 -0
  70. package/lib/front_end/third_party/codemirror.next/chunk/vue.js +2 -0
  71. package/lib/front_end/third_party/codemirror.next/chunk/vue.js.map +1 -0
  72. package/lib/front_end/third_party/codemirror.next/chunk/wast.js +2 -0
  73. package/lib/front_end/third_party/codemirror.next/chunk/wast.js.map +1 -0
  74. package/lib/front_end/third_party/codemirror.next/chunk/xml.js +2 -0
  75. package/lib/front_end/third_party/codemirror.next/chunk/xml.js.map +1 -0
  76. package/lib/front_end/third_party/codemirror.next/codemirror.next.d.ts +8057 -0
  77. package/lib/front_end/third_party/codemirror.next/codemirror.next.js +2 -0
  78. package/lib/front_end/third_party/codemirror.next/codemirror.next.js.map +1 -0
  79. package/lib/front_end/third_party/codemirror.next/package.json +43 -0
  80. package/lib/front_end/third_party/codemirror.next/rebuild.sh +6 -0
  81. package/lib/front_end/third_party/codemirror.next/rollup.config.mjs +49 -0
  82. package/lib/front_end/third_party/source-map-scopes-codec/LICENSE +26 -0
  83. package/lib/front_end/third_party/source-map-scopes-codec/README.chromium +31 -0
  84. package/lib/front_end/third_party/source-map-scopes-codec/package/CONTRIBUTING.md +33 -0
  85. package/lib/front_end/third_party/source-map-scopes-codec/package/LICENSE +26 -0
  86. package/lib/front_end/third_party/source-map-scopes-codec/package/README.md +64 -0
  87. package/lib/front_end/third_party/source-map-scopes-codec/package/_dist/src/builder/builder.d.ts +62 -0
  88. package/lib/front_end/third_party/source-map-scopes-codec/package/_dist/src/builder/builder.d.ts.map +1 -0
  89. package/lib/front_end/third_party/source-map-scopes-codec/package/_dist/src/builder/safe_builder.d.ts +37 -0
  90. package/lib/front_end/third_party/source-map-scopes-codec/package/_dist/src/builder/safe_builder.d.ts.map +1 -0
  91. package/lib/front_end/third_party/source-map-scopes-codec/package/_dist/src/decode/decode.d.ts +29 -0
  92. package/lib/front_end/third_party/source-map-scopes-codec/package/_dist/src/decode/decode.d.ts.map +1 -0
  93. package/lib/front_end/third_party/source-map-scopes-codec/package/_dist/src/encode/encode.d.ts +8 -0
  94. package/lib/front_end/third_party/source-map-scopes-codec/package/_dist/src/encode/encode.d.ts.map +1 -0
  95. package/lib/front_end/third_party/source-map-scopes-codec/package/_dist/src/mod.d.ts +6 -0
  96. package/lib/front_end/third_party/source-map-scopes-codec/package/_dist/src/mod.d.ts.map +1 -0
  97. package/lib/front_end/third_party/source-map-scopes-codec/package/deno.json +21 -0
  98. package/lib/front_end/third_party/source-map-scopes-codec/package/package.json +14 -0
  99. package/lib/front_end/third_party/source-map-scopes-codec/package/src/builder/builder.js +196 -0
  100. package/lib/front_end/third_party/source-map-scopes-codec/package/src/builder/builder.js.map +1 -0
  101. package/lib/front_end/third_party/source-map-scopes-codec/package/src/builder/builder.ts +262 -0
  102. package/lib/front_end/third_party/source-map-scopes-codec/package/src/builder/safe_builder.js +235 -0
  103. package/lib/front_end/third_party/source-map-scopes-codec/package/src/builder/safe_builder.js.map +1 -0
  104. package/lib/front_end/third_party/source-map-scopes-codec/package/src/builder/safe_builder.ts +359 -0
  105. package/lib/front_end/third_party/source-map-scopes-codec/package/src/codec.js +39 -0
  106. package/lib/front_end/third_party/source-map-scopes-codec/package/src/codec.js.map +1 -0
  107. package/lib/front_end/third_party/source-map-scopes-codec/package/src/codec.ts +53 -0
  108. package/lib/front_end/third_party/source-map-scopes-codec/package/src/decode/decode.js +438 -0
  109. package/lib/front_end/third_party/source-map-scopes-codec/package/src/decode/decode.js.map +1 -0
  110. package/lib/front_end/third_party/source-map-scopes-codec/package/src/decode/decode.ts +539 -0
  111. package/lib/front_end/third_party/source-map-scopes-codec/package/src/encode/encode.js +23 -0
  112. package/lib/front_end/third_party/source-map-scopes-codec/package/src/encode/encode.js.map +1 -0
  113. package/lib/front_end/third_party/source-map-scopes-codec/package/src/encode/encode.ts +35 -0
  114. package/lib/front_end/third_party/source-map-scopes-codec/package/src/encode/encoder.js +257 -0
  115. package/lib/front_end/third_party/source-map-scopes-codec/package/src/encode/encoder.js.map +1 -0
  116. package/lib/front_end/third_party/source-map-scopes-codec/package/src/encode/encoder.ts +348 -0
  117. package/lib/front_end/third_party/source-map-scopes-codec/package/src/mod.js +8 -0
  118. package/lib/front_end/third_party/source-map-scopes-codec/package/src/mod.js.map +1 -0
  119. package/lib/front_end/third_party/source-map-scopes-codec/package/src/mod.ts +20 -0
  120. package/lib/front_end/third_party/source-map-scopes-codec/package/src/scopes-tsconfig.json +8 -0
  121. package/lib/front_end/third_party/source-map-scopes-codec/package/src/scopes.d.ts +184 -0
  122. package/lib/front_end/third_party/source-map-scopes-codec/package/src/util.js +9 -0
  123. package/lib/front_end/third_party/source-map-scopes-codec/package/src/util.js.map +1 -0
  124. package/lib/front_end/third_party/source-map-scopes-codec/package/src/util.ts +12 -0
  125. package/lib/front_end/third_party/source-map-scopes-codec/package/src/vlq.js +82 -0
  126. package/lib/front_end/third_party/source-map-scopes-codec/package/src/vlq.js.map +1 -0
  127. package/lib/front_end/third_party/source-map-scopes-codec/package/src/vlq.ts +99 -0
  128. package/lib/front_end/third_party/source-map-scopes-codec/source-map-scopes-codec.ts +5 -0
  129. package/lib/front_end/ui/legacy/theme_support/ThemeSupport.ts +222 -0
  130. package/lib/front_end/ui/legacy/theme_support/theme_support.ts +5 -0
  131. package/package.json +5 -5
  132. package/patches/chrome-devtools-frontend+1.0.1533544.patch +1549 -20
@@ -0,0 +1,84 @@
1
+ // Copyright 2024 The Chromium Authors
2
+ // Use of this source code is governed by a BSD-style license that can be
3
+ // found in the LICENSE file.
4
+
5
+ import * as Common from '../../core/common/common.js';
6
+ import * as SDK from '../../core/sdk/sdk.js';
7
+
8
+ import {resolveScopeChain} from './NamesResolver.js';
9
+
10
+ /**
11
+ * This class is responsible for resolving / updating the scope chain for a specific {@link SDK.DebuggerModel.CallFrame}
12
+ * instance.
13
+ *
14
+ * There are several sources that can influence the scope view:
15
+ * - Debugger plugins can provide the whole scope info (e.g. from DWARF)
16
+ * - Source Maps can provide OR augment scope info
17
+ *
18
+ * Source maps can be enabled/disabled dynamically and debugger plugins can attach debug info after the fact.
19
+ *
20
+ * This class tracks all that and sends events with the latest scope chain for a specific call frame.
21
+ */
22
+ export class ScopeChainModel extends Common.ObjectWrapper.ObjectWrapper<EventTypes> {
23
+ readonly #callFrame: SDK.DebuggerModel.CallFrame;
24
+
25
+ /** We use the `Throttler` here to make sure that `#boundUpdate` is not run multiple times simultanously */
26
+ readonly #throttler = new Common.Throttler.Throttler(5);
27
+ readonly #boundUpdate = this.#update.bind(this);
28
+
29
+ constructor(callFrame: SDK.DebuggerModel.CallFrame) {
30
+ super();
31
+ this.#callFrame = callFrame;
32
+ this.#callFrame.debuggerModel.addEventListener(
33
+ SDK.DebuggerModel.Events.DebugInfoAttached, this.#debugInfoAttached, this);
34
+ this.#callFrame.debuggerModel.sourceMapManager().addEventListener(
35
+ SDK.SourceMapManager.Events.SourceMapAttached, this.#sourceMapAttached, this);
36
+
37
+ void this.#throttler.schedule(this.#boundUpdate);
38
+ }
39
+
40
+ dispose(): void {
41
+ this.#callFrame.debuggerModel.removeEventListener(
42
+ SDK.DebuggerModel.Events.DebugInfoAttached, this.#debugInfoAttached, this);
43
+ this.#callFrame.debuggerModel.sourceMapManager().removeEventListener(
44
+ SDK.SourceMapManager.Events.SourceMapAttached, this.#sourceMapAttached, this);
45
+ this.listeners?.clear();
46
+ }
47
+
48
+ async #update(): Promise<void> {
49
+ const scopeChain = await resolveScopeChain(this.#callFrame);
50
+ this.dispatchEventToListeners(Events.SCOPE_CHAIN_UPDATED, new ScopeChain(scopeChain));
51
+ }
52
+
53
+ #debugInfoAttached(event: Common.EventTarget.EventTargetEvent<SDK.Script.Script>): void {
54
+ if (event.data === this.#callFrame.script) {
55
+ void this.#throttler.schedule(this.#boundUpdate);
56
+ }
57
+ }
58
+
59
+ #sourceMapAttached(event: Common.EventTarget
60
+ .EventTargetEvent<{client: SDK.Script.Script, sourceMap: SDK.SourceMap.SourceMap}>): void {
61
+ if (event.data.client === this.#callFrame.script) {
62
+ void this.#throttler.schedule(this.#boundUpdate);
63
+ }
64
+ }
65
+ }
66
+
67
+ export const enum Events {
68
+ SCOPE_CHAIN_UPDATED = 'ScopeChainUpdated',
69
+ }
70
+
71
+ export interface EventTypes {
72
+ [Events.SCOPE_CHAIN_UPDATED]: ScopeChain;
73
+ }
74
+
75
+ /**
76
+ * A scope chain ready to be shown in the UI with debugging info applied.
77
+ */
78
+ export class ScopeChain {
79
+ readonly scopeChain: SDK.DebuggerModel.ScopeChainEntry[];
80
+
81
+ constructor(scopeChain: SDK.DebuggerModel.ScopeChainEntry[]) {
82
+ this.scopeChain = scopeChain;
83
+ }
84
+ }
@@ -0,0 +1,11 @@
1
+ // Copyright 2022 The Chromium Authors
2
+ // Use of this source code is governed by a BSD-style license that can be
3
+ // found in the LICENSE file.
4
+
5
+ import * as NamesResolver from './NamesResolver.js';
6
+ import * as ScopeChainModel from './ScopeChainModel.js';
7
+
8
+ export {
9
+ NamesResolver,
10
+ ScopeChainModel,
11
+ };
@@ -0,0 +1,53 @@
1
+ // Copyright 2025 The Chromium Authors
2
+ // Use of this source code is governed by a BSD-style license that can be
3
+ // found in the LICENSE file.
4
+
5
+ import type * as Common from '../../core/common/common.js';
6
+ import type * as SDK from '../../core/sdk/sdk.js';
7
+ import type * as Workspace from '../workspace/workspace.js';
8
+
9
+ export interface StackTrace extends Common.EventTarget.EventTarget<EventTypes> {
10
+ readonly syncFragment: Fragment;
11
+ readonly asyncFragments: readonly AsyncFragment[];
12
+ }
13
+
14
+ export interface Fragment {
15
+ readonly frames: readonly Frame[];
16
+ }
17
+
18
+ export interface AsyncFragment extends Fragment {
19
+ readonly description: string;
20
+ }
21
+
22
+ export interface Frame {
23
+ readonly url?: string;
24
+ readonly uiSourceCode?: Workspace.UISourceCode.UISourceCode;
25
+ readonly name?: string;
26
+ readonly line: number;
27
+ readonly column: number;
28
+
29
+ readonly missingDebugInfo?: MissingDebugInfo;
30
+ }
31
+
32
+ export const enum MissingDebugInfoType {
33
+ /** No debug information at all for the call frame */
34
+ NO_INFO = 'NO_INFO',
35
+
36
+ /** Some debug information available, but it references files with debug information we were not able to retrieve */
37
+ PARTIAL_INFO = 'PARTIAL_INFO',
38
+ }
39
+
40
+ export type MissingDebugInfo = {
41
+ type: MissingDebugInfoType.NO_INFO,
42
+ }|{
43
+ type: MissingDebugInfoType.PARTIAL_INFO,
44
+ missingDebugFiles: SDK.DebuggerModel.MissingDebugFiles[],
45
+ };
46
+
47
+ export const enum Events {
48
+ UPDATED = 'UPDATED',
49
+ }
50
+
51
+ export interface EventTypes {
52
+ [Events.UPDATED]: void;
53
+ }
@@ -0,0 +1,85 @@
1
+ // Copyright 2025 The Chromium Authors
2
+ // Use of this source code is governed by a BSD-style license that can be
3
+ // found in the LICENSE file.
4
+
5
+ import * as Common from '../../core/common/common.js';
6
+ import type * as Workspace from '../workspace/workspace.js';
7
+
8
+ import type * as StackTrace from './stack_trace.js';
9
+ import type {FrameNode} from './Trie.js';
10
+
11
+ export class StackTraceImpl extends Common.ObjectWrapper.ObjectWrapper<StackTrace.StackTrace.EventTypes> implements
12
+ StackTrace.StackTrace.StackTrace {
13
+ readonly syncFragment: FragmentImpl;
14
+ readonly asyncFragments: readonly AsyncFragmentImpl[];
15
+
16
+ constructor(syncFragment: FragmentImpl, asyncFragments: AsyncFragmentImpl[]) {
17
+ super();
18
+ this.syncFragment = syncFragment;
19
+ this.asyncFragments = asyncFragments;
20
+
21
+ syncFragment.stackTraces.add(this);
22
+ this.asyncFragments.forEach(asyncFragment => asyncFragment.fragment.stackTraces.add(this));
23
+ }
24
+ }
25
+
26
+ export class FragmentImpl implements StackTrace.StackTrace.Fragment {
27
+ readonly node: FrameNode;
28
+ readonly stackTraces = new Set<StackTraceImpl>();
29
+
30
+ /**
31
+ * Fragments are deduplicated based on the node.
32
+ *
33
+ * In turn, each fragment can be part of multiple stack traces.
34
+ */
35
+ static getOrCreate(node: FrameNode): FragmentImpl {
36
+ if (!node.fragment) {
37
+ node.fragment = new FragmentImpl(node);
38
+ }
39
+ return node.fragment;
40
+ }
41
+
42
+ private constructor(node: FrameNode) {
43
+ this.node = node;
44
+ }
45
+
46
+ get frames(): FrameImpl[] {
47
+ const frames: FrameImpl[] = [];
48
+
49
+ for (const node of this.node.getCallStack()) {
50
+ frames.push(...node.frames);
51
+ }
52
+
53
+ return frames;
54
+ }
55
+ }
56
+
57
+ export class AsyncFragmentImpl implements StackTrace.StackTrace.AsyncFragment {
58
+ constructor(readonly description: string, readonly fragment: FragmentImpl) {
59
+ }
60
+
61
+ get frames(): StackTrace.StackTrace.Frame[] {
62
+ return this.fragment.frames;
63
+ }
64
+ }
65
+
66
+ export class FrameImpl implements StackTrace.StackTrace.Frame {
67
+ readonly url?: string;
68
+ readonly uiSourceCode?: Workspace.UISourceCode.UISourceCode;
69
+ readonly name?: string;
70
+ readonly line: number;
71
+ readonly column: number;
72
+
73
+ readonly missingDebugInfo?: StackTrace.StackTrace.MissingDebugInfo;
74
+
75
+ constructor(
76
+ url: string|undefined, uiSourceCode: Workspace.UISourceCode.UISourceCode|undefined, name: string|undefined,
77
+ line: number, column: number, missingDebugInfo?: StackTrace.StackTrace.MissingDebugInfo) {
78
+ this.url = url;
79
+ this.uiSourceCode = uiSourceCode;
80
+ this.name = name;
81
+ this.line = line;
82
+ this.column = column;
83
+ this.missingDebugInfo = missingDebugInfo;
84
+ }
85
+ }
@@ -0,0 +1,128 @@
1
+ // Copyright 2025 The Chromium Authors
2
+ // Use of this source code is governed by a BSD-style license that can be
3
+ // found in the LICENSE file.
4
+
5
+ import * as SDK from '../../core/sdk/sdk.js';
6
+ import type * as Protocol from '../../generated/protocol.js';
7
+
8
+ // eslint-disable-next-line @devtools/es-modules-import
9
+ import * as StackTrace from './stack_trace.js';
10
+ import {AsyncFragmentImpl, FragmentImpl, FrameImpl, StackTraceImpl} from './StackTraceImpl.js';
11
+ import {type FrameNode, type RawFrame, Trie} from './Trie.js';
12
+
13
+ /**
14
+ * A stack trace translation function.
15
+ *
16
+ * Any implementation must return an array with the same length as `frames`.
17
+ */
18
+ export type TranslateRawFrames = (frames: readonly RawFrame[], target: SDK.Target.Target) => Promise<
19
+ Array<Array<Pick<StackTrace.StackTrace.Frame, 'url'|'uiSourceCode'|'name'|'line'|'column'|'missingDebugInfo'>>>>;
20
+
21
+ /**
22
+ * The {@link StackTraceModel} is a thin wrapper around a fragment trie.
23
+ *
24
+ * We want to store stack trace fragments per target so a SDKModel is the natural choice.
25
+ */
26
+ export class StackTraceModel extends SDK.SDKModel.SDKModel<unknown> {
27
+ readonly #trie = new Trie();
28
+
29
+ /** @returns the {@link StackTraceModel} for the target, or the model for the primaryPageTarget when passing null/undefined */
30
+ static #modelForTarget(target: SDK.Target.Target|null|undefined): StackTraceModel {
31
+ const model = (target ?? SDK.TargetManager.TargetManager.instance().primaryPageTarget())?.model(StackTraceModel);
32
+ if (!model) {
33
+ throw new Error('Unable to find StackTraceModel');
34
+ }
35
+ return model;
36
+ }
37
+
38
+ async createFromProtocolRuntime(stackTrace: Protocol.Runtime.StackTrace, rawFramesToUIFrames: TranslateRawFrames):
39
+ Promise<StackTrace.StackTrace.StackTrace> {
40
+ const translatePromises: Array<Promise<unknown>> = [];
41
+
42
+ const fragment = this.#createFragment(stackTrace.callFrames);
43
+ translatePromises.push(this.#translateFragment(fragment, rawFramesToUIFrames));
44
+
45
+ const asyncFragments: AsyncFragmentImpl[] = [];
46
+ const debuggerModel = this.target().model(SDK.DebuggerModel.DebuggerModel);
47
+ if (debuggerModel) {
48
+ for await (const {stackTrace: asyncStackTrace, target} of debuggerModel.iterateAsyncParents(stackTrace)) {
49
+ const model = StackTraceModel.#modelForTarget(target);
50
+ const asyncFragment = model.#createFragment(asyncStackTrace.callFrames);
51
+ translatePromises.push(model.#translateFragment(asyncFragment, rawFramesToUIFrames));
52
+ asyncFragments.push(new AsyncFragmentImpl(asyncStackTrace.description ?? '', asyncFragment));
53
+ }
54
+ }
55
+
56
+ await Promise.all(translatePromises);
57
+
58
+ return new StackTraceImpl(fragment, asyncFragments);
59
+ }
60
+
61
+ /** Trigger re-translation of all fragments with the provide script in their call stack */
62
+ async scriptInfoChanged(script: SDK.Script.Script, translateRawFrames: TranslateRawFrames): Promise<void> {
63
+ const translatePromises: Array<Promise<unknown>> = [];
64
+ let stackTracesToUpdate = new Set<StackTraceImpl>();
65
+
66
+ for (const fragment of this.#affectedFragments(script)) {
67
+ // We trigger re-translation only for fragments of leaf-nodes. Any fragment along the ancestor-chain
68
+ // is re-translated as a side-effect.
69
+ // We just need to remember the stack traces of the skipped over fragments, so we can send the
70
+ // UPDATED event also to them.
71
+ if (fragment.node.children.length === 0) {
72
+ translatePromises.push(this.#translateFragment(fragment, translateRawFrames));
73
+ }
74
+ stackTracesToUpdate = stackTracesToUpdate.union(fragment.stackTraces);
75
+ }
76
+
77
+ await Promise.all(translatePromises);
78
+
79
+ for (const stackTrace of stackTracesToUpdate) {
80
+ stackTrace.dispatchEventToListeners(StackTrace.StackTrace.Events.UPDATED);
81
+ }
82
+ }
83
+
84
+ #createFragment(frames: RawFrame[]): FragmentImpl {
85
+ return FragmentImpl.getOrCreate(this.#trie.insert(frames));
86
+ }
87
+
88
+ async #translateFragment(fragment: FragmentImpl, rawFramesToUIFrames: TranslateRawFrames): Promise<void> {
89
+ const rawFrames = fragment.node.getCallStack().map(node => node.rawFrame).toArray();
90
+ const uiFrames = await rawFramesToUIFrames(rawFrames, this.target());
91
+ console.assert(rawFrames.length === uiFrames.length, 'Broken rawFramesToUIFrames implementation');
92
+
93
+ let i = 0;
94
+ for (const node of fragment.node.getCallStack()) {
95
+ node.frames = uiFrames[i++].map(
96
+ frame => new FrameImpl(
97
+ frame.url, frame.uiSourceCode, frame.name, frame.line, frame.column, frame.missingDebugInfo));
98
+ }
99
+ }
100
+
101
+ #affectedFragments(script: SDK.Script.Script): Set<FragmentImpl> {
102
+ // 1. Collect branches with the matching script.
103
+ const affectedBranches = new Set<FrameNode>();
104
+ this.#trie.walk(null, node => {
105
+ // scriptId has precedence, but if the frame does not have one, check the URL.
106
+ if (node.rawFrame.scriptId === script.scriptId ||
107
+ (!node.rawFrame.scriptId && node.rawFrame.url === script.sourceURL)) {
108
+ affectedBranches.add(node);
109
+ return false;
110
+ }
111
+ return true;
112
+ });
113
+
114
+ // 2. For each branch collect all the fragments.
115
+ const fragments = new Set<FragmentImpl>();
116
+ for (const branch of affectedBranches) {
117
+ this.#trie.walk(branch, node => {
118
+ if (node.fragment) {
119
+ fragments.add(node.fragment);
120
+ }
121
+ return true;
122
+ });
123
+ }
124
+ return fragments;
125
+ }
126
+ }
127
+
128
+ SDK.SDKModel.SDKModel.register(StackTraceModel, {capabilities: SDK.Target.Capability.NONE, autostart: false});
@@ -0,0 +1,163 @@
1
+ // Copyright 2025 The Chromium Authors
2
+ // Use of this source code is governed by a BSD-style license that can be
3
+ // found in the LICENSE file.
4
+
5
+ import type * as Protocol from '../../generated/protocol.js';
6
+
7
+ import type {FragmentImpl, FrameImpl} from './StackTraceImpl.js';
8
+
9
+ /**
10
+ * Intentionally very close to a {@link Protocol.Runtime.CallFrame} but with optional `scriptId`.
11
+ */
12
+ export interface RawFrame {
13
+ readonly scriptId?: Protocol.Runtime.ScriptId;
14
+ readonly url?: string;
15
+ readonly functionName?: string;
16
+ readonly lineNumber: number;
17
+ readonly columnNumber: number;
18
+ }
19
+
20
+ /**
21
+ * @returns whether the frame is a V8 builtin frame e.g. Array.map. Builtin frames
22
+ * have neither source position nor script or URL. They only have a name.
23
+ */
24
+ export function isBuiltinFrame(rawFrame: RawFrame): boolean {
25
+ return rawFrame.lineNumber === -1 && rawFrame.columnNumber === -1 && !Boolean(rawFrame.scriptId) &&
26
+ !Boolean(rawFrame.url);
27
+ }
28
+
29
+ interface FrameNodeBase<ChildT, ParentT> {
30
+ readonly parent: ParentT;
31
+ readonly children: ChildT[];
32
+ }
33
+
34
+ type RootFrameNode = FrameNodeBase<WeakRef<FrameNode>, null>;
35
+ type AnyFrameNode = FrameNode|RootFrameNode;
36
+
37
+ export class FrameNode implements FrameNodeBase<FrameNode, AnyFrameNode> {
38
+ readonly parent: AnyFrameNode;
39
+ readonly children: FrameNode[] = [];
40
+
41
+ readonly rawFrame: RawFrame;
42
+ frames: FrameImpl[] = [];
43
+
44
+ fragment?: FragmentImpl;
45
+
46
+ constructor(rawFrame: RawFrame, parent: AnyFrameNode) {
47
+ this.rawFrame = rawFrame;
48
+ this.parent = parent;
49
+ }
50
+
51
+ /**
52
+ * Produces the ancestor chain. Including `this` but excluding the `RootFrameNode`.
53
+ */
54
+ * getCallStack(): Generator<FrameNode> {
55
+ // The `RootFrameNode` doesn't have an actual frame attached, that's why we check for `node.parent` instead of `node`.
56
+ for (let node: AnyFrameNode|null = this; node.parent; node = node.parent) {
57
+ yield node;
58
+ }
59
+ }
60
+ }
61
+
62
+ /**
63
+ * Stores stack trace fragments in a trie, but does not own them/keep them alive.
64
+ */
65
+ export class Trie {
66
+ readonly #root: RootFrameNode = {parent: null, children: []};
67
+
68
+ /**
69
+ * Most sources produce stack traces in "top-to-bottom" order, so that is what this method expects.
70
+ *
71
+ * @returns The {@link FrameNode} corresponding to the top-most stack frame.
72
+ */
73
+ insert(frames: RawFrame[]): FrameNode {
74
+ if (frames.length === 0) {
75
+ throw new Error('Trie.insert called with an empty frames array.');
76
+ }
77
+
78
+ let currentNode: AnyFrameNode = this.#root;
79
+ for (let i = frames.length - 1; i >= 0; --i) {
80
+ currentNode = this.#insert(currentNode, frames[i]);
81
+ }
82
+ return currentNode as FrameNode;
83
+ }
84
+
85
+ /**
86
+ * Inserts `rawFrame` into the children of the provided node if not already there.
87
+ *
88
+ * @returns the child node corresponding to `rawFrame`.
89
+ */
90
+ #insert(node: AnyFrameNode, rawFrame: RawFrame): FrameNode {
91
+ let i = 0;
92
+ for (; i < node.children.length; ++i) {
93
+ const maybeChild = node.children[i];
94
+ const child = maybeChild instanceof WeakRef ? maybeChild.deref() : maybeChild;
95
+ if (!child) {
96
+ continue;
97
+ }
98
+
99
+ const compareResult = compareRawFrames(child.rawFrame, rawFrame);
100
+ if (compareResult === 0) {
101
+ return child;
102
+ }
103
+ if (compareResult > 0) {
104
+ break;
105
+ }
106
+ }
107
+
108
+ const newNode = new FrameNode(rawFrame, node);
109
+ if (node.parent) {
110
+ node.children.splice(i, 0, newNode);
111
+ } else {
112
+ node.children.splice(i, 0, new WeakRef(newNode));
113
+ }
114
+ return newNode;
115
+ }
116
+
117
+ /**
118
+ * Traverses the trie in pre-order.
119
+ *
120
+ * @param node Start at `node` or `null` to start with the children of the root.
121
+ * @param visit Called on each node in the trie. Return `true` if the visitor should descend into child nodes of the provided node.
122
+ */
123
+ walk(node: FrameNode|null, visit: (node: FrameNode) => boolean): void {
124
+ const stack =
125
+ node ? [node] : [...this.#root.children].map(ref => ref.deref()).filter(node => Boolean(node)) as FrameNode[];
126
+
127
+ for (let node = stack.pop(); node; node = stack.pop()) {
128
+ const visitChildren = visit(node);
129
+ if (visitChildren) {
130
+ // Pushing the children in reverse means the "left-most" child is visited first (i.e. pre-order).
131
+ for (let i = node.children.length - 1; i >= 0; --i) {
132
+ stack.push(node.children[i]);
133
+ }
134
+ }
135
+ }
136
+ }
137
+ }
138
+
139
+ /**
140
+ * @returns a number < 0, 0 or > 0, if the `a` is smaller then, equal or greater then `b`.
141
+ */
142
+ export function compareRawFrames(a: RawFrame, b: RawFrame): number {
143
+ const scriptIdCompare = (a.scriptId ?? '').localeCompare(b.scriptId ?? '');
144
+ if (scriptIdCompare !== 0) {
145
+ return scriptIdCompare;
146
+ }
147
+
148
+ const urlCompare = (a.url ?? '').localeCompare(b.url ?? '');
149
+ if (urlCompare !== 0) {
150
+ return urlCompare;
151
+ }
152
+
153
+ const nameCompare = (a.functionName ?? '').localeCompare(b.functionName ?? '');
154
+ if (nameCompare !== 0) {
155
+ return nameCompare;
156
+ }
157
+
158
+ if (a.lineNumber !== b.lineNumber) {
159
+ return a.lineNumber - b.lineNumber;
160
+ }
161
+
162
+ return a.columnNumber - b.columnNumber;
163
+ }
@@ -0,0 +1,9 @@
1
+ // Copyright 2025 The Chromium Authors
2
+ // Use of this source code is governed by a BSD-style license that can be
3
+ // found in the LICENSE file.
4
+
5
+ import * as StackTrace from './StackTrace.js';
6
+
7
+ export {
8
+ StackTrace,
9
+ };
@@ -0,0 +1,13 @@
1
+ // Copyright 2025 The Chromium Authors
2
+ // Use of this source code is governed by a BSD-style license that can be
3
+ // found in the LICENSE file.
4
+
5
+ import * as StackTraceImpl from './StackTraceImpl.js';
6
+ import * as StackTraceModel from './StackTraceModel.js';
7
+ import * as Trie from './Trie.js';
8
+
9
+ export {
10
+ StackTraceImpl,
11
+ StackTraceModel,
12
+ Trie,
13
+ };