chrome-ai-bridge 1.0.3 → 1.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/node_modules/chrome-devtools-frontend/front_end/core/common/Base64.js +20 -2
- package/build/node_modules/chrome-devtools-frontend/front_end/core/common/Gzip.js +11 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/core/common/Object.js +6 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/core/common/ParsedURL.js +3 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/core/common/ResourceType.js +6 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/core/common/Settings.js +18 -8
- package/build/node_modules/chrome-devtools-frontend/front_end/core/host/InspectorFrontendHostStub.js +3 -3
- package/build/node_modules/chrome-devtools-frontend/front_end/core/host/ResourceLoader.js +1 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/core/host/UserMetrics.js +17 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/core/platform/ArrayUtilities.js +10 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/core/platform/StringUtilities.js +63 -12
- package/build/node_modules/chrome-devtools-frontend/front_end/core/protocol_client/CDPConnection.js +1 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/core/protocol_client/InspectorBackend.js +4 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/CSSMatchedStyles.js +44 -9
- package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/CSSMetadata.js +6 -6
- package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/CSSModel.js +1 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/DOMModel.js +169 -12
- package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/DebuggerModel.js +2 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/IsolateManager.js +6 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/NetworkManager.js +18 -4
- package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/NetworkRequest.js +7 -21
- package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/OverlayModel.js +17 -5
- package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/RehydratingConnection.js +5 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/ResourceTreeModel.js +8 -5
- package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/SourceMap.js +14 -2
- package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/SourceMapManager.js +1 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/SourceMapScopesInfo.js +11 -4
- package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/Target.js +3 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/generated/ARIAProperties.js +1 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/generated/Deprecation.js +1 -16
- package/build/node_modules/chrome-devtools-frontend/front_end/generated/InspectorBackendCommands.js +35 -14
- package/build/node_modules/chrome-devtools-frontend/front_end/generated/SupportedCSSProperties.js +197 -101
- package/build/node_modules/chrome-devtools-frontend/front_end/models/ai_assistance/data_formatters/NetworkRequestFormatter.js +2 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.js +10 -16
- package/build/node_modules/chrome-devtools-frontend/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.js +97 -26
- package/build/node_modules/chrome-devtools-frontend/front_end/models/ai_assistance/performance/AICallTree.js +35 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/models/annotations/AnnotationRepository.js +163 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/models/annotations/AnnotationType.js +10 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/models/annotations/annotations.js +5 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/models/bindings/CompilerScriptMapping.js +5 -3
- package/build/node_modules/chrome-devtools-frontend/front_end/models/bindings/DebuggerWorkspaceBinding.js +7 -3
- package/build/node_modules/chrome-devtools-frontend/front_end/models/emulation/DeviceModeModel.js +1 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/models/emulation/EmulatedDevices.js +14 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/models/formatter/FormatterWorkerPool.js +8 -5
- package/build/node_modules/chrome-devtools-frontend/front_end/models/greendev/Prototypes.js +33 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/models/greendev/greendev.js +4 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/models/stack_trace/StackTraceImpl.js +70 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/models/stack_trace/StackTraceModel.js +82 -30
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/EventsSerializer.js +7 -2
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/LanternComputationData.js +2 -2
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/Processor.js +18 -19
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/Styles.js +12 -4
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/extras/Initiators.js +46 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/extras/TraceTree.js +4 -3
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/extras/extras.js +1 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/handlers/LargestImagePaintHandler.js +2 -2
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/handlers/LayoutShiftsHandler.js +1 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/handlers/MetaHandler.js +6 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/handlers/NetworkRequestsHandler.js +10 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/handlers/PageLoadMetricsHandler.js +44 -27
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/helpers/Timing.js +9 -2
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/Common.js +1 -6
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/LCPBreakdown.js +2 -2
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/LCPDiscovery.js +2 -4
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/NetworkDependencyTree.js +3 -2
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/RenderBlocking.js +1 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/types/TraceEvents.js +30 -11
- package/build/node_modules/chrome-devtools-frontend/front_end/third_party/source-map-scopes-codec/package/src/decode/decode.js +28 -13
- package/build/node_modules/chrome-devtools-frontend/front_end/third_party/source-map-scopes-codec/package/src/encode/encoder.js +1 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/third_party/source-map-scopes-codec/package/src/scopes.js +4 -0
- package/build/src/tools/chatgpt-web.js +68 -49
- package/build/src/tools/gemini-web.js +66 -22
- package/build/src/tools/pages.js +0 -1
- package/package.json +1 -1
|
@@ -8,8 +8,8 @@ import { bytes, millis } from './UnitFormatters.js';
|
|
|
8
8
|
/**
|
|
9
9
|
* For a given frame ID and navigation ID, returns the LCP Event and the LCP Request, if the resource was an image.
|
|
10
10
|
*/
|
|
11
|
-
function getLCPData(parsedTrace, frameId,
|
|
12
|
-
const navMetrics = parsedTrace.data.PageLoadMetrics.metricScoresByFrameId.get(frameId)?.get(
|
|
11
|
+
function getLCPData(parsedTrace, frameId, navigation) {
|
|
12
|
+
const navMetrics = parsedTrace.data.PageLoadMetrics.metricScoresByFrameId.get(frameId)?.get(navigation);
|
|
13
13
|
if (!navMetrics) {
|
|
14
14
|
return null;
|
|
15
15
|
}
|
|
@@ -18,12 +18,14 @@ function getLCPData(parsedTrace, frameId, navigationId) {
|
|
|
18
18
|
return null;
|
|
19
19
|
}
|
|
20
20
|
const lcpEvent = metric?.event;
|
|
21
|
-
if (!lcpEvent || !Trace.Types.Events.
|
|
21
|
+
if (!lcpEvent || !Trace.Types.Events.isAnyLargestContentfulPaintCandidate(lcpEvent)) {
|
|
22
22
|
return null;
|
|
23
23
|
}
|
|
24
|
+
const navigationId = navigation.args.data?.navigationId;
|
|
24
25
|
return {
|
|
25
26
|
lcpEvent,
|
|
26
|
-
lcpRequest: parsedTrace.data.LargestImagePaint.lcpRequestByNavigationId.get(navigationId)
|
|
27
|
+
lcpRequest: navigationId ? parsedTrace.data.LargestImagePaint.lcpRequestByNavigationId.get(navigationId) :
|
|
28
|
+
undefined,
|
|
27
29
|
metricScore: metric,
|
|
28
30
|
};
|
|
29
31
|
}
|
|
@@ -68,14 +70,14 @@ export class PerformanceInsightFormatter {
|
|
|
68
70
|
* Information about LCP which we pass to the LLM for all insights that relate to LCP.
|
|
69
71
|
*/
|
|
70
72
|
#lcpMetricSharedContext() {
|
|
71
|
-
if (!this.#insight.
|
|
73
|
+
if (!this.#insight.navigation) {
|
|
72
74
|
// No navigation ID = no LCP.
|
|
73
75
|
return '';
|
|
74
76
|
}
|
|
75
|
-
if (!this.#insight.frameId || !this.#insight.
|
|
77
|
+
if (!this.#insight.frameId || !this.#insight.navigation) {
|
|
76
78
|
return '';
|
|
77
79
|
}
|
|
78
|
-
const data = getLCPData(this.#parsedTrace, this.#insight.frameId, this.#insight.
|
|
80
|
+
const data = getLCPData(this.#parsedTrace, this.#insight.frameId, this.#insight.navigation);
|
|
79
81
|
if (!data) {
|
|
80
82
|
return '';
|
|
81
83
|
}
|
|
@@ -97,12 +99,6 @@ export class PerformanceInsightFormatter {
|
|
|
97
99
|
return parts.join('\n');
|
|
98
100
|
}
|
|
99
101
|
insightIsSupported() {
|
|
100
|
-
// Although our types don't show it, Insights can end up as Errors if there
|
|
101
|
-
// is an issue in the processing stage. In this case we should gracefully
|
|
102
|
-
// ignore this error.
|
|
103
|
-
if (this.#insight instanceof Error) {
|
|
104
|
-
return false;
|
|
105
|
-
}
|
|
106
102
|
return this.#description().length > 0;
|
|
107
103
|
}
|
|
108
104
|
getSuggestions() {
|
|
@@ -179,7 +175,7 @@ export class PerformanceInsightFormatter {
|
|
|
179
175
|
{ title: 'How can I reduce the amount of legacy JavaScript on my page?' },
|
|
180
176
|
];
|
|
181
177
|
default:
|
|
182
|
-
throw new Error(
|
|
178
|
+
throw new Error(`Unknown insight key '${this.#insight.insightKey}'`);
|
|
183
179
|
}
|
|
184
180
|
}
|
|
185
181
|
/**
|
|
@@ -228,8 +224,6 @@ export class PerformanceInsightFormatter {
|
|
|
228
224
|
potentialRootCauses.push(animationInfoOutput.map(l => ' '.repeat(4) + l).join('\n'));
|
|
229
225
|
});
|
|
230
226
|
rootCauses.unsizedImages.forEach(img => {
|
|
231
|
-
// TODO(b/413284569): if we store a nice human readable name for this
|
|
232
|
-
// image in the trace metadata, we can do something much nicer here.
|
|
233
227
|
const url = img.paintImageEvent.args.data.url;
|
|
234
228
|
const nodeName = img.paintImageEvent.args.data.nodeName;
|
|
235
229
|
const extraText = url ? `url: ${this.#formatUrl(url)}` : `id: ${img.backendNodeId}`;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Copyright 2025 The Chromium Authors
|
|
2
2
|
// Use of this source code is governed by a BSD-style license that can be
|
|
3
3
|
// found in the LICENSE file.
|
|
4
|
+
import * as Annotations from '../../annotations/annotations.js';
|
|
4
5
|
import * as CrUXManager from '../../crux-manager/crux-manager.js';
|
|
5
6
|
import * as Trace from '../../trace/trace.js';
|
|
6
7
|
import { AIQueries } from '../performance/AIQueries.js';
|
|
@@ -12,6 +13,8 @@ export class PerformanceTraceFormatter {
|
|
|
12
13
|
#parsedTrace;
|
|
13
14
|
#insightSet;
|
|
14
15
|
#eventsSerializer;
|
|
16
|
+
#formattedFunctionCodes = new Set();
|
|
17
|
+
resolveFunctionCode;
|
|
15
18
|
constructor(focus) {
|
|
16
19
|
this.#focus = focus;
|
|
17
20
|
this.#parsedTrace = focus.parsedTrace;
|
|
@@ -101,19 +104,19 @@ export class PerformanceTraceFormatter {
|
|
|
101
104
|
parts.push('\n# Available insight sets\n');
|
|
102
105
|
parts.push('The following is a list of insight sets. An insight set covers a specific part of the trace, split by navigations. The insights within each insight set are specific to that part of the trace. Be sure to consider the insight set id and bounds when calling functions. If no specific insight set or navigation is mentioned, assume the user is referring to the first one.');
|
|
103
106
|
for (const insightSet of parsedTrace.insights?.values() ?? []) {
|
|
104
|
-
const lcp =
|
|
105
|
-
const cls =
|
|
106
|
-
const inp =
|
|
107
|
+
const lcp = Trace.Insights.Common.getLCP(insightSet);
|
|
108
|
+
const cls = Trace.Insights.Common.getCLS(insightSet);
|
|
109
|
+
const inp = Trace.Insights.Common.getINP(insightSet);
|
|
107
110
|
parts.push(`\n## insight set id: ${insightSet.id}\n`);
|
|
108
111
|
parts.push(`URL: ${insightSet.url}`);
|
|
109
112
|
parts.push(`Bounds: ${this.serializeBounds(insightSet.bounds)}`);
|
|
110
113
|
if (lcp || cls || inp) {
|
|
111
114
|
parts.push('Metrics (lab / observed):');
|
|
112
115
|
if (lcp) {
|
|
113
|
-
const nodeId = insightSet
|
|
116
|
+
const nodeId = insightSet.model.LCPBreakdown?.lcpEvent?.args.data?.nodeId;
|
|
114
117
|
const nodeIdText = nodeId !== undefined ? `, nodeId: ${nodeId}` : '';
|
|
115
118
|
parts.push(` - LCP: ${Math.round(lcp.value / 1000)} ms, event: ${this.serializeEvent(lcp.event)}${nodeIdText}`);
|
|
116
|
-
const subparts = insightSet
|
|
119
|
+
const subparts = insightSet.model.LCPBreakdown?.subparts;
|
|
117
120
|
if (subparts) {
|
|
118
121
|
const serializeSubpart = (subpart) => {
|
|
119
122
|
return `${micros(subpart.range)}, bounds: ${this.serializeBounds(subpart)}`;
|
|
@@ -135,6 +138,13 @@ export class PerformanceTraceFormatter {
|
|
|
135
138
|
if (cls) {
|
|
136
139
|
const eventText = cls.worstClusterEvent ? `, event: ${this.serializeEvent(cls.worstClusterEvent)}` : '';
|
|
137
140
|
parts.push(` - CLS: ${cls.value.toFixed(2)}${eventText}`);
|
|
141
|
+
if (Annotations.AnnotationRepository.annotationsEnabled()) {
|
|
142
|
+
const worstClusterEvent = cls.worstClusterEvent;
|
|
143
|
+
const layoutShiftData = worstClusterEvent?.worstShiftEvent?.args?.data;
|
|
144
|
+
if (layoutShiftData?.impacted_nodes && layoutShiftData.impacted_nodes?.length > 0) {
|
|
145
|
+
Annotations.AnnotationRepository.instance().addElementsAnnotation('This element is impacted by a layout shift', layoutShiftData.impacted_nodes[0].node_id.toString());
|
|
146
|
+
}
|
|
147
|
+
}
|
|
138
148
|
}
|
|
139
149
|
}
|
|
140
150
|
else {
|
|
@@ -178,7 +188,7 @@ export class PerformanceTraceFormatter {
|
|
|
178
188
|
}
|
|
179
189
|
return parts.join('\n');
|
|
180
190
|
}
|
|
181
|
-
#formatFactByInsightSet(options) {
|
|
191
|
+
async #formatFactByInsightSet(options) {
|
|
182
192
|
const { insights, title, description, empty, cb } = options;
|
|
183
193
|
const lines = [`# ${title}\n`];
|
|
184
194
|
if (description) {
|
|
@@ -190,7 +200,7 @@ export class PerformanceTraceFormatter {
|
|
|
190
200
|
if (multipleInsightSets) {
|
|
191
201
|
lines.push(`## insight set id: ${insightSet.id}\n`);
|
|
192
202
|
}
|
|
193
|
-
lines.push((cb(insightSet) ?? empty) + '\n');
|
|
203
|
+
lines.push((await cb(insightSet) ?? empty) + '\n');
|
|
194
204
|
}
|
|
195
205
|
}
|
|
196
206
|
else {
|
|
@@ -204,18 +214,18 @@ export class PerformanceTraceFormatter {
|
|
|
204
214
|
insights: parsedTrace.insights,
|
|
205
215
|
title: 'Critical network requests',
|
|
206
216
|
empty: 'none',
|
|
207
|
-
cb: insightSet => {
|
|
217
|
+
cb: async (insightSet) => {
|
|
208
218
|
const criticalRequests = [];
|
|
209
219
|
const walkRequest = (node) => {
|
|
210
220
|
criticalRequests.push(node.request);
|
|
211
221
|
node.children.forEach(walkRequest);
|
|
212
222
|
};
|
|
213
|
-
insightSet.model.NetworkDependencyTree
|
|
223
|
+
insightSet.model.NetworkDependencyTree?.rootNodes.forEach(walkRequest);
|
|
214
224
|
return criticalRequests.length ? this.formatNetworkRequests(criticalRequests, { verbose: false }) : null;
|
|
215
225
|
},
|
|
216
226
|
});
|
|
217
227
|
}
|
|
218
|
-
#serializeBottomUpRootNode(rootNode, limit) {
|
|
228
|
+
async #serializeBottomUpRootNode(rootNode, limit) {
|
|
219
229
|
// Sorted by selfTime.
|
|
220
230
|
// No nodes less than 1 ms.
|
|
221
231
|
// Limit.
|
|
@@ -223,15 +233,20 @@ export class PerformanceTraceFormatter {
|
|
|
223
233
|
.filter(n => n.totalTime >= 1)
|
|
224
234
|
.sort((a, b) => b.selfTime - a.selfTime)
|
|
225
235
|
.slice(0, limit);
|
|
236
|
+
const callFrames = [];
|
|
226
237
|
function nodeToText(node) {
|
|
227
238
|
const event = node.event;
|
|
228
239
|
let frame;
|
|
229
240
|
if (Trace.Types.Events.isProfileCall(event)) {
|
|
230
241
|
frame = event.callFrame;
|
|
242
|
+
if (node.selfTime >= 100 && callFrames.length < 3) {
|
|
243
|
+
callFrames.push(frame);
|
|
244
|
+
}
|
|
231
245
|
}
|
|
232
246
|
else {
|
|
233
247
|
frame = Trace.Helpers.Trace.getStackTraceTopCallFrameInEventPayload(event);
|
|
234
248
|
}
|
|
249
|
+
// TODO(crbug.com/452333154): this is not source mapped.
|
|
235
250
|
let source = Trace.Name.forEntry(event);
|
|
236
251
|
if (frame?.url) {
|
|
237
252
|
source += ` (url: ${frame.url}`;
|
|
@@ -245,7 +260,8 @@ export class PerformanceTraceFormatter {
|
|
|
245
260
|
}
|
|
246
261
|
return `- self: ${millis(node.selfTime)}, total: ${millis(node.totalTime)}, source: ${source}`;
|
|
247
262
|
}
|
|
248
|
-
return topNodes.map(node => nodeToText.call(this, node)).join('\n')
|
|
263
|
+
return topNodes.map(node => nodeToText.call(this, node)).join('\n') +
|
|
264
|
+
await this.#serializeRelevantFunctions(callFrames);
|
|
249
265
|
}
|
|
250
266
|
#getSerializeBottomUpRootNodeFormat(limit) {
|
|
251
267
|
return `This is the bottom-up summary for the entire trace. Only the top ${limit} activities (sorted by self time) are shown. An activity is all the aggregated time spent on the same type of work. For example, it can be all the time spent in a specific JavaScript function, or all the time spent in a specific browser rendering stage (like layout, v8 compile, parsing html). "Self time" represents the aggregated time spent directly in an activity, across all occurrences. "Total time" represents the aggregated time spent in an activity or any of its children.`;
|
|
@@ -258,9 +274,9 @@ export class PerformanceTraceFormatter {
|
|
|
258
274
|
title: 'Main thread bottom-up summary',
|
|
259
275
|
description: this.#getSerializeBottomUpRootNodeFormat(limit),
|
|
260
276
|
empty: 'no activity',
|
|
261
|
-
cb: insightSet => {
|
|
277
|
+
cb: async (insightSet) => {
|
|
262
278
|
const rootNode = AIQueries.mainThreadActivityBottomUpSingleNavigation(insightSet.navigation?.args.data?.navigationId, insightSet.bounds, parsedTrace);
|
|
263
|
-
return rootNode ? this.#serializeBottomUpRootNode(rootNode, limit) : null;
|
|
279
|
+
return rootNode ? await this.#serializeBottomUpRootNode(rootNode, limit) : null;
|
|
264
280
|
},
|
|
265
281
|
});
|
|
266
282
|
}
|
|
@@ -283,7 +299,7 @@ export class PerformanceTraceFormatter {
|
|
|
283
299
|
insights: parsedTrace.insights,
|
|
284
300
|
title: '3rd party summary',
|
|
285
301
|
empty: 'no 3rd parties',
|
|
286
|
-
cb: insightSet => {
|
|
302
|
+
cb: async (insightSet) => {
|
|
287
303
|
const thirdPartySummaries = Trace.Extras.ThirdParties.summarizeByThirdParty(parsedTrace.data, insightSet.bounds);
|
|
288
304
|
return thirdPartySummaries.length ? this.#formatThirdPartyEntitySummaries(thirdPartySummaries) : null;
|
|
289
305
|
},
|
|
@@ -295,7 +311,7 @@ export class PerformanceTraceFormatter {
|
|
|
295
311
|
insights: parsedTrace.insights,
|
|
296
312
|
title: 'Longest tasks',
|
|
297
313
|
empty: 'none',
|
|
298
|
-
cb: insightSet => {
|
|
314
|
+
cb: async (insightSet) => {
|
|
299
315
|
const longestTaskTrees = AIQueries.longestTasks(insightSet.navigation?.args.data?.navigationId, insightSet.bounds, parsedTrace, 3);
|
|
300
316
|
if (!longestTaskTrees?.length) {
|
|
301
317
|
return null;
|
|
@@ -342,7 +358,7 @@ export class PerformanceTraceFormatter {
|
|
|
342
358
|
}
|
|
343
359
|
return results.join('\n');
|
|
344
360
|
}
|
|
345
|
-
formatMainThreadTrackSummary(bounds) {
|
|
361
|
+
async formatMainThreadTrackSummary(bounds) {
|
|
346
362
|
if (!this.#parsedTrace.insights) {
|
|
347
363
|
return 'No main thread activity found';
|
|
348
364
|
}
|
|
@@ -351,14 +367,14 @@ export class PerformanceTraceFormatter {
|
|
|
351
367
|
const topDownTree = AIQueries.mainThreadActivityTopDown(insightSet?.navigation?.args.data?.navigationId, bounds, this.#parsedTrace);
|
|
352
368
|
if (topDownTree) {
|
|
353
369
|
results.push('# Top-down main thread summary');
|
|
354
|
-
results.push(this.formatCallTree(topDownTree, 2 /* headerLevel */));
|
|
370
|
+
results.push(await this.formatCallTree(topDownTree, 2 /* headerLevel */));
|
|
355
371
|
}
|
|
356
372
|
const bottomUpRootNode = AIQueries.mainThreadActivityBottomUp(bounds, this.#parsedTrace);
|
|
357
373
|
if (bottomUpRootNode) {
|
|
358
374
|
results.push('# Bottom-up main thread summary');
|
|
359
375
|
const limit = 20;
|
|
360
376
|
results.push(this.#getSerializeBottomUpRootNodeFormat(limit));
|
|
361
|
-
results.push(this.#serializeBottomUpRootNode(bottomUpRootNode, limit));
|
|
377
|
+
results.push(await this.#serializeBottomUpRootNode(bottomUpRootNode, limit));
|
|
362
378
|
}
|
|
363
379
|
const thirdPartySummaries = Trace.Extras.ThirdParties.summarizeByThirdParty(this.#parsedTrace.data, bounds);
|
|
364
380
|
if (thirdPartySummaries.length) {
|
|
@@ -390,8 +406,19 @@ export class PerformanceTraceFormatter {
|
|
|
390
406
|
}
|
|
391
407
|
return results.join('\n\n');
|
|
392
408
|
}
|
|
393
|
-
formatCallTree(tree, headerLevel = 1) {
|
|
394
|
-
|
|
409
|
+
async formatCallTree(tree, headerLevel = 1) {
|
|
410
|
+
let result = `${tree.serialize(headerLevel)}\n\nIMPORTANT: Never show eventKey to the user.\n`;
|
|
411
|
+
const relevantCallFrames = [];
|
|
412
|
+
if (tree.selectedNode && Trace.Types.Events.isProfileCall(tree.selectedNode.event)) {
|
|
413
|
+
relevantCallFrames.push(tree.selectedNode.event.callFrame);
|
|
414
|
+
}
|
|
415
|
+
const topCallFrameByTotalTime = tree.topCallFrameByTotalTime();
|
|
416
|
+
if (topCallFrameByTotalTime) {
|
|
417
|
+
relevantCallFrames.push(topCallFrameByTotalTime);
|
|
418
|
+
}
|
|
419
|
+
relevantCallFrames.push(...tree.topCallFramesBySelfTime(3));
|
|
420
|
+
result += await this.#serializeRelevantFunctions(relevantCallFrames);
|
|
421
|
+
return result;
|
|
395
422
|
}
|
|
396
423
|
formatNetworkRequests(requests, options) {
|
|
397
424
|
if (requests.length === 0) {
|
|
@@ -424,7 +451,7 @@ export class PerformanceTraceFormatter {
|
|
|
424
451
|
const initiators = [];
|
|
425
452
|
let cur = request;
|
|
426
453
|
while (cur) {
|
|
427
|
-
const initiator =
|
|
454
|
+
const initiator = Trace.Extras.Initiators.getNetworkInitiator(parsedTrace.data, cur);
|
|
428
455
|
if (initiator) {
|
|
429
456
|
// Should never happen, but if it did that would be an infinite loop.
|
|
430
457
|
if (initiators.includes(initiator)) {
|
|
@@ -445,7 +472,7 @@ export class PerformanceTraceFormatter {
|
|
|
445
472
|
* talk to jacktfranklin@.
|
|
446
473
|
*/
|
|
447
474
|
#networkRequestVerbosely(request, options) {
|
|
448
|
-
const { url, statusCode, initialPriority, priority, fromServiceWorker, mimeType, responseHeaders, syntheticData, protocol } = request.args.data;
|
|
475
|
+
const { url, requestId, statusCode, initialPriority, priority, fromServiceWorker, mimeType, responseHeaders, syntheticData, protocol } = request.args.data;
|
|
449
476
|
const parsedTrace = this.#parsedTrace;
|
|
450
477
|
const titlePrefix = `## ${options?.customTitle ?? 'Network request'}`;
|
|
451
478
|
// Note: unlike other agents, we do have the ability to include
|
|
@@ -464,7 +491,7 @@ export class PerformanceTraceFormatter {
|
|
|
464
491
|
const mainThreadProcessingDuration = startTimesForLifecycle.processingCompletedAt - startTimesForLifecycle.downloadCompletedAt;
|
|
465
492
|
const downloadTime = syntheticData.finishTime - syntheticData.downloadStart;
|
|
466
493
|
const renderBlocking = Trace.Helpers.Network.isSyntheticNetworkRequestEventRenderBlocking(request);
|
|
467
|
-
const initiator =
|
|
494
|
+
const initiator = Trace.Extras.Initiators.getNetworkInitiator(parsedTrace.data, request);
|
|
468
495
|
const priorityLines = [];
|
|
469
496
|
if (initialPriority === priority) {
|
|
470
497
|
priorityLines.push(`Priority: ${priority}`);
|
|
@@ -483,7 +510,7 @@ export class PerformanceTraceFormatter {
|
|
|
483
510
|
const initiatorUrls = initiators.map(initiator => initiator.args.data.url);
|
|
484
511
|
const eventKey = this.#eventsSerializer.keyForEvent(request);
|
|
485
512
|
const eventKeyLine = eventKey ? `eventKey: ${eventKey}\n` : '';
|
|
486
|
-
return `${titlePrefix}: ${url}
|
|
513
|
+
return `${titlePrefix}: ${url}${Annotations.AnnotationRepository.annotationsEnabled() ? `\nrequestId: ${requestId}` : ''}
|
|
487
514
|
${eventKeyLine}Timings:
|
|
488
515
|
- Queued at: ${micros(startTimesForLifecycle.queuedAt)}
|
|
489
516
|
- Request sent at: ${micros(startTimesForLifecycle.requestSentAt)}
|
|
@@ -648,17 +675,61 @@ The order of headers corresponds to an internal fixed list. If a header is not p
|
|
|
648
675
|
];
|
|
649
676
|
return parts.join(';');
|
|
650
677
|
}
|
|
678
|
+
resolveFunctionCodeAtLocation(url, line, column) {
|
|
679
|
+
if (!this.resolveFunctionCode) {
|
|
680
|
+
throw new Error('missing resolveFunctionCode');
|
|
681
|
+
}
|
|
682
|
+
return this.resolveFunctionCode(url, line, column);
|
|
683
|
+
}
|
|
651
684
|
formatFunctionCode(code) {
|
|
685
|
+
return this.#getFormattedFunctionCodeExplainer() + '\n\n' + this.#formatFunctionCode(code);
|
|
686
|
+
}
|
|
687
|
+
#getFormattedFunctionCodeExplainer() {
|
|
688
|
+
return 'The following are markdown block(s) of code that ran in the page, each representing a separate function. <FUNCTION_START> and <FUNCTION_END> marks the exact function declaration, and everything outside that is provided for additional context. Comments at the end of each line indicate the runtime performance cost of that code. Do not show the user the function markers or the additional context.';
|
|
689
|
+
}
|
|
690
|
+
#functionCodeToKey(code) {
|
|
691
|
+
return code.functionBounds.uiSourceCode.url() + ':' + code.functionBounds.range.toString();
|
|
692
|
+
}
|
|
693
|
+
#hasFormattedFunctionCode(code) {
|
|
694
|
+
return this.#formattedFunctionCodes.has(this.#functionCodeToKey(code));
|
|
695
|
+
}
|
|
696
|
+
#formatFunctionCode(code) {
|
|
697
|
+
this.#formattedFunctionCodes.add(this.#functionCodeToKey(code));
|
|
652
698
|
const { startLine, startColumn } = code.range;
|
|
653
699
|
const { startLine: contextStartLine, startColumn: contextStartColumn, endLine: contextEndLine, endColumn: contextEndColumn } = code.rangeWithContext;
|
|
654
|
-
const name = code.functionBounds.name;
|
|
700
|
+
const name = code.functionBounds.name || '(anonymous)';
|
|
655
701
|
const url = code.functionBounds.uiSourceCode.url();
|
|
656
702
|
const parts = [];
|
|
657
703
|
parts.push(`${name} @ ${url}:${startLine}:${startColumn}. With added context, chunk is from ${contextStartLine}:${contextStartColumn} to ${contextEndLine}:${contextEndColumn}`);
|
|
658
|
-
parts.push('\nThe following is a markdown block of JavaScript. <FUNCTION_START> and <FUNCTION_END> marks the exact function declaration, and everything outside that is provided for additional context. Comments at the end of each line indicate the runtime performance cost of that code. Do not show the user the function markers or the additional context.\n');
|
|
659
704
|
parts.push('```');
|
|
660
705
|
parts.push(code.codeWithContext);
|
|
661
706
|
parts.push('```');
|
|
662
707
|
return parts.join('\n');
|
|
663
708
|
}
|
|
709
|
+
/**
|
|
710
|
+
* Appends the code of each call frame's function, but only if the function was not
|
|
711
|
+
* serialized previously.
|
|
712
|
+
*/
|
|
713
|
+
async #serializeRelevantFunctions(callFrames) {
|
|
714
|
+
const resolveFunctionCode = this.resolveFunctionCode;
|
|
715
|
+
if (!resolveFunctionCode) {
|
|
716
|
+
return '';
|
|
717
|
+
}
|
|
718
|
+
const functionCodeStrings = [];
|
|
719
|
+
const functionCodes = await Promise.all(callFrames.map(frame => resolveFunctionCode(frame.url, frame.lineNumber, frame.columnNumber)));
|
|
720
|
+
for (const code of functionCodes) {
|
|
721
|
+
if (code && !this.#hasFormattedFunctionCode(code)) {
|
|
722
|
+
functionCodeStrings.push(this.#formatFunctionCode(code));
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
if (!functionCodeStrings.length) {
|
|
726
|
+
return '';
|
|
727
|
+
}
|
|
728
|
+
return '\n' + [
|
|
729
|
+
this.#getFormattedFunctionCodeExplainer(),
|
|
730
|
+
functionCodeStrings.length > 1 ? `Here are ${functionCodeStrings.length} relevant functions:` :
|
|
731
|
+
`Here is a relevant function:`,
|
|
732
|
+
...functionCodeStrings,
|
|
733
|
+
].join('\n\n');
|
|
734
|
+
}
|
|
664
735
|
}
|
|
@@ -319,6 +319,41 @@ export class AICallTree {
|
|
|
319
319
|
}
|
|
320
320
|
return line;
|
|
321
321
|
}
|
|
322
|
+
topCallFramesBySelfTime(limit) {
|
|
323
|
+
const functionNodesByCallFrame = new Map();
|
|
324
|
+
this.breadthFirstWalk(this.rootNode.children().values(), node => {
|
|
325
|
+
if (Trace.Types.Events.isProfileCall(node.event)) {
|
|
326
|
+
const callFrame = node.event.callFrame;
|
|
327
|
+
const callFrameKey = `${callFrame.scriptId}:${callFrame.lineNumber}:${callFrame.columnNumber}`;
|
|
328
|
+
const array = functionNodesByCallFrame.get(callFrameKey) ?? [];
|
|
329
|
+
array.push(node);
|
|
330
|
+
functionNodesByCallFrame.set(callFrameKey, array);
|
|
331
|
+
}
|
|
332
|
+
});
|
|
333
|
+
return [...functionNodesByCallFrame.values()]
|
|
334
|
+
.map(nodes => {
|
|
335
|
+
return {
|
|
336
|
+
callFrame: nodes[0].event.callFrame,
|
|
337
|
+
selfTime: nodes.reduce((total, cur) => total + cur.selfTime, 0),
|
|
338
|
+
};
|
|
339
|
+
})
|
|
340
|
+
.sort((a, b) => b.selfTime - a.selfTime)
|
|
341
|
+
.slice(0, limit)
|
|
342
|
+
.map(({ callFrame }) => callFrame);
|
|
343
|
+
}
|
|
344
|
+
topCallFrameByTotalTime() {
|
|
345
|
+
let topChild = null;
|
|
346
|
+
let topProfileCallEvent = null;
|
|
347
|
+
for (const child of this.rootNode.children().values()) {
|
|
348
|
+
if (Trace.Types.Events.isProfileCall(child.event)) {
|
|
349
|
+
if (!topChild || child.totalTime > topChild.totalTime) {
|
|
350
|
+
topChild = child;
|
|
351
|
+
topProfileCallEvent = child.event;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
return topProfileCallEvent?.callFrame ?? null;
|
|
356
|
+
}
|
|
322
357
|
// Only used for debugging.
|
|
323
358
|
logDebug() {
|
|
324
359
|
const str = this.serialize();
|
|
@@ -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
|
+
import * as Common from '../../core/common/common.js';
|
|
5
|
+
import * as GreenDev from '../greendev/greendev.js';
|
|
6
|
+
import { AnnotationType } from './AnnotationType.js';
|
|
7
|
+
export class AnnotationRepository {
|
|
8
|
+
static #instance = null;
|
|
9
|
+
static #hasRepliedGreenDevDisabled = false;
|
|
10
|
+
static #hasShownFlagWarning = false;
|
|
11
|
+
#events = new Common.ObjectWrapper.ObjectWrapper();
|
|
12
|
+
#annotationData = [];
|
|
13
|
+
#nextId = 0;
|
|
14
|
+
static instance() {
|
|
15
|
+
if (!AnnotationRepository.#instance) {
|
|
16
|
+
AnnotationRepository.#instance = new AnnotationRepository();
|
|
17
|
+
}
|
|
18
|
+
return AnnotationRepository.#instance;
|
|
19
|
+
}
|
|
20
|
+
static annotationsEnabled() {
|
|
21
|
+
const enabled = GreenDev.Prototypes.instance().isEnabled('aiAnnotations');
|
|
22
|
+
// TODO(finnur): Fix race when Repository is created before feature flags have been set properly.
|
|
23
|
+
if (!enabled) {
|
|
24
|
+
this.#hasRepliedGreenDevDisabled = true;
|
|
25
|
+
}
|
|
26
|
+
else if (this.#hasRepliedGreenDevDisabled && !this.#hasShownFlagWarning) {
|
|
27
|
+
console.warn('Flag controlling GreenDev has flipped from false to true. ' +
|
|
28
|
+
'Only some callers will expect GreenDev to be enabled, which can lead to unexpected results.');
|
|
29
|
+
this.#hasShownFlagWarning = true;
|
|
30
|
+
}
|
|
31
|
+
return Boolean(enabled);
|
|
32
|
+
}
|
|
33
|
+
addEventListener(eventType, listener, thisObject) {
|
|
34
|
+
if (!AnnotationRepository.annotationsEnabled()) {
|
|
35
|
+
console.warn('Received request to add event listener with annotations disabled');
|
|
36
|
+
}
|
|
37
|
+
return this.#events.addEventListener(eventType, listener, thisObject);
|
|
38
|
+
}
|
|
39
|
+
getAnnotationDataByType(type) {
|
|
40
|
+
if (!AnnotationRepository.annotationsEnabled()) {
|
|
41
|
+
console.warn('Received query for annotation types with annotations disabled');
|
|
42
|
+
return [];
|
|
43
|
+
}
|
|
44
|
+
const annotations = this.#annotationData.filter(annotation => annotation.type === type);
|
|
45
|
+
return annotations;
|
|
46
|
+
}
|
|
47
|
+
getAnnotationDataById(id) {
|
|
48
|
+
if (!AnnotationRepository.annotationsEnabled()) {
|
|
49
|
+
console.warn('Received query for annotation type with annotations disabled');
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
return this.#annotationData.find(annotation => annotation.id === id);
|
|
53
|
+
}
|
|
54
|
+
#getExistingAnnotation(type, anchor) {
|
|
55
|
+
const annotations = this.getAnnotationDataByType(type);
|
|
56
|
+
const annotation = annotations.find(annotation => {
|
|
57
|
+
if (typeof anchor === 'string') {
|
|
58
|
+
return annotation.lookupId === anchor;
|
|
59
|
+
}
|
|
60
|
+
switch (type) {
|
|
61
|
+
case AnnotationType.ELEMENT_NODE: {
|
|
62
|
+
const elementAnnotation = annotation;
|
|
63
|
+
return elementAnnotation.anchor === anchor;
|
|
64
|
+
}
|
|
65
|
+
case AnnotationType.NETWORK_REQUEST_SUBPANEL_HEADERS: {
|
|
66
|
+
const networkRequestDetailsAnnotation = annotation;
|
|
67
|
+
return networkRequestDetailsAnnotation.anchor === anchor;
|
|
68
|
+
}
|
|
69
|
+
default:
|
|
70
|
+
console.warn('[AnnotationRepository] Unknown AnnotationType', type);
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
return annotation;
|
|
75
|
+
}
|
|
76
|
+
#updateExistingAnnotationLabel(label, type, anchor) {
|
|
77
|
+
const annotation = this.#getExistingAnnotation(type, anchor);
|
|
78
|
+
if (annotation) {
|
|
79
|
+
// TODO(finnur): This should work for annotations that have not been displayed yet,
|
|
80
|
+
// but we need to also notify the AnnotationManager for those that have been shown.
|
|
81
|
+
annotation.message = label;
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
addElementsAnnotation(label, anchor, anchorToString) {
|
|
87
|
+
if (!AnnotationRepository.annotationsEnabled()) {
|
|
88
|
+
console.warn('Received annotation registration with annotations disabled');
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
if (this.#updateExistingAnnotationLabel(label, AnnotationType.ELEMENT_NODE, anchor)) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
const annotationData = {
|
|
95
|
+
id: this.#nextId++,
|
|
96
|
+
type: AnnotationType.ELEMENT_NODE,
|
|
97
|
+
message: label,
|
|
98
|
+
lookupId: typeof anchor === 'string' ? anchor : '',
|
|
99
|
+
anchor: typeof anchor !== 'string' ? anchor : undefined,
|
|
100
|
+
anchorToString,
|
|
101
|
+
};
|
|
102
|
+
this.#annotationData.push(annotationData);
|
|
103
|
+
// eslint-disable-next-line no-console
|
|
104
|
+
console.log('[AnnotationRepository] Added element annotation:', label, {
|
|
105
|
+
annotationData,
|
|
106
|
+
annotations: this.#annotationData.length,
|
|
107
|
+
});
|
|
108
|
+
this.#events.dispatchEventToListeners("AnnotationAdded" /* Events.ANNOTATION_ADDED */, annotationData);
|
|
109
|
+
}
|
|
110
|
+
addNetworkRequestAnnotation(label, anchor, anchorToString) {
|
|
111
|
+
if (!AnnotationRepository.annotationsEnabled()) {
|
|
112
|
+
console.warn('Received annotation registration with annotations disabled');
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
// We only need to update the NETWORK_REQUEST_SUBPANEL_HEADERS because the
|
|
116
|
+
// NETWORK_REQUEST Annotation has no meaningful label.
|
|
117
|
+
if (this.#updateExistingAnnotationLabel(label, AnnotationType.NETWORK_REQUEST_SUBPANEL_HEADERS, anchor)) {
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
const annotationData = {
|
|
121
|
+
id: this.#nextId++,
|
|
122
|
+
type: AnnotationType.NETWORK_REQUEST,
|
|
123
|
+
message: '',
|
|
124
|
+
lookupId: typeof anchor === 'string' ? anchor : '',
|
|
125
|
+
anchor: typeof anchor !== 'string' ? anchor : undefined,
|
|
126
|
+
anchorToString,
|
|
127
|
+
};
|
|
128
|
+
this.#annotationData.push(annotationData);
|
|
129
|
+
// eslint-disable-next-line no-console
|
|
130
|
+
console.log('[AnnotationRepository] Added annotation:', label, {
|
|
131
|
+
annotationData,
|
|
132
|
+
annotations: this.#annotationData.length,
|
|
133
|
+
});
|
|
134
|
+
this.#events.dispatchEventToListeners("AnnotationAdded" /* Events.ANNOTATION_ADDED */, annotationData);
|
|
135
|
+
const annotationDetailsData = {
|
|
136
|
+
id: this.#nextId++,
|
|
137
|
+
type: AnnotationType.NETWORK_REQUEST_SUBPANEL_HEADERS,
|
|
138
|
+
message: label,
|
|
139
|
+
lookupId: typeof anchor === 'string' ? anchor : '',
|
|
140
|
+
anchor: typeof anchor !== 'string' ? anchor : undefined,
|
|
141
|
+
anchorToString,
|
|
142
|
+
};
|
|
143
|
+
this.#annotationData.push(annotationDetailsData);
|
|
144
|
+
this.#events.dispatchEventToListeners("AnnotationAdded" /* Events.ANNOTATION_ADDED */, annotationDetailsData);
|
|
145
|
+
}
|
|
146
|
+
deleteAllAnnotations() {
|
|
147
|
+
this.#annotationData = [];
|
|
148
|
+
this.#events.dispatchEventToListeners("AllAnnotationsDeleted" /* Events.ALL_ANNOTATIONS_DELETED */);
|
|
149
|
+
// eslint-disable-next-line no-console
|
|
150
|
+
console.log('[AnnotationRepository] Deleting all annotations');
|
|
151
|
+
}
|
|
152
|
+
deleteAnnotation(id) {
|
|
153
|
+
const index = this.#annotationData.findIndex(annotation => annotation.id === id);
|
|
154
|
+
if (index === -1) {
|
|
155
|
+
console.warn(`[AnnotationRepository] Could not find annotation with id ${id}`);
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
this.#annotationData.splice(index, 1);
|
|
159
|
+
this.#events.dispatchEventToListeners("AnnotationDeleted" /* Events.ANNOTATION_DELETED */, { id });
|
|
160
|
+
// eslint-disable-next-line no-console
|
|
161
|
+
console.log(`[AnnotationRepository] Deleted annotation with id ${id}`);
|
|
162
|
+
}
|
|
163
|
+
}
|
package/build/node_modules/chrome-devtools-frontend/front_end/models/annotations/AnnotationType.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
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
|
+
export var AnnotationType;
|
|
5
|
+
(function (AnnotationType) {
|
|
6
|
+
AnnotationType[AnnotationType["ELEMENT_NODE"] = 0] = "ELEMENT_NODE";
|
|
7
|
+
AnnotationType[AnnotationType["STYLE_RULE"] = 1] = "STYLE_RULE";
|
|
8
|
+
AnnotationType[AnnotationType["NETWORK_REQUEST"] = 2] = "NETWORK_REQUEST";
|
|
9
|
+
AnnotationType[AnnotationType["NETWORK_REQUEST_SUBPANEL_HEADERS"] = 3] = "NETWORK_REQUEST_SUBPANEL_HEADERS";
|
|
10
|
+
})(AnnotationType || (AnnotationType = {}));
|
|
@@ -229,6 +229,7 @@ export class CompilerScriptMapping {
|
|
|
229
229
|
if (!sourceMap) {
|
|
230
230
|
return null;
|
|
231
231
|
}
|
|
232
|
+
await sourceMap.waitForScopeInfo();
|
|
232
233
|
const { lineNumber, columnNumber } = script.rawLocationToRelativeLocation(rawLocation);
|
|
233
234
|
const { url, scope } = sourceMap.findOriginalFunctionScope({ line: lineNumber, column: columnNumber }) ?? {};
|
|
234
235
|
if (!scope || !url) {
|
|
@@ -254,21 +255,22 @@ export class CompilerScriptMapping {
|
|
|
254
255
|
const range = new TextUtils.TextRange.TextRange(scope.start.line, scope.start.column, scope.end.line, scope.end.column);
|
|
255
256
|
return new Workspace.UISourceCode.UIFunctionBounds(uiSourceCode, range, name);
|
|
256
257
|
}
|
|
257
|
-
translateRawFramesStep(rawFrames, translatedFrames) {
|
|
258
|
+
async translateRawFramesStep(rawFrames, translatedFrames) {
|
|
258
259
|
const frame = rawFrames[0];
|
|
259
260
|
if (StackTraceImpl.Trie.isBuiltinFrame(frame)) {
|
|
260
261
|
return false;
|
|
261
262
|
}
|
|
262
|
-
const sourceMapWithScopeInfoForFrame = (rawFrame) => {
|
|
263
|
+
const sourceMapWithScopeInfoForFrame = async (rawFrame) => {
|
|
263
264
|
const script = this.#debuggerModel.scriptForId(rawFrame.scriptId ?? '');
|
|
264
265
|
if (!script || this.#stubUISourceCodes.has(script)) {
|
|
265
266
|
// Use fallback while source map is being loaded.
|
|
266
267
|
return null;
|
|
267
268
|
}
|
|
268
269
|
const sourceMap = script.sourceMap();
|
|
270
|
+
await sourceMap?.waitForScopeInfo();
|
|
269
271
|
return sourceMap?.hasScopeInfo() ? { sourceMap, script } : null;
|
|
270
272
|
};
|
|
271
|
-
const sourceMapAndScript = sourceMapWithScopeInfoForFrame(frame);
|
|
273
|
+
const sourceMapAndScript = await sourceMapWithScopeInfoForFrame(frame);
|
|
272
274
|
if (!sourceMapAndScript) {
|
|
273
275
|
return false;
|
|
274
276
|
}
|