chrome-devtools-frontend 1.0.1706026 → 1.0.1708524

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 (86) hide show
  1. package/.agents/skills/code-review/SKILL.md +121 -0
  2. package/front_end/core/sdk/DOMModel.ts +13 -6
  3. package/front_end/core/sdk/NetworkRequest.ts +43 -7
  4. package/front_end/core/sdk/SourceMap.ts +4 -13
  5. package/front_end/core/sdk/SourceMapScopesInfo.ts +134 -86
  6. package/front_end/core/text_utils/ContentData.ts +18 -1
  7. package/front_end/entrypoints/heap_snapshot_worker/ContextAnalyzer.ts +102 -4
  8. package/front_end/generated/InspectorBackendCommands.ts +7 -1
  9. package/front_end/generated/SupportedCSSProperties.ts +11 -2
  10. package/front_end/generated/protocol-mapping.d.ts +31 -0
  11. package/front_end/generated/protocol-proxy-api.d.ts +32 -0
  12. package/front_end/generated/protocol.ts +11 -0
  13. package/front_end/models/ai_assistance/AiAgent2.ts +1 -0
  14. package/front_end/models/ai_assistance/data_formatters/LighthouseFormatter.ts +26 -3
  15. package/front_end/models/ai_assistance/data_formatters/NetworkRequestFormatter.ts +15 -16
  16. package/front_end/models/ai_assistance/skills/Skill.ts +1 -2
  17. package/front_end/models/ai_assistance/skills/SkillRegistry.ts +2 -0
  18. package/front_end/models/ai_assistance/skills/lighthouse.md +33 -0
  19. package/front_end/models/ai_assistance/tools/GetLighthouseAudits.ts +6 -6
  20. package/front_end/models/ai_assistance/tools/RunLighthouse.ts +8 -5
  21. package/front_end/models/bindings/CompilerScriptMapping.ts +102 -45
  22. package/front_end/models/bindings/DebuggerLanguagePlugins.ts +25 -33
  23. package/front_end/models/bindings/DebuggerWorkspaceBinding.ts +33 -39
  24. package/front_end/models/bindings/StylesSourceMapping.ts +3 -0
  25. package/front_end/models/extensions/ExtensionEndpoint.ts +27 -6
  26. package/front_end/models/extensions/LanguageExtensionEndpoint.ts +106 -21
  27. package/front_end/models/extensions/RecorderExtensionEndpoint.ts +4 -2
  28. package/front_end/models/javascript_metadata/NativeFunctions.ts +6 -2
  29. package/front_end/models/persistence/NetworkPersistenceManager.ts +4 -3
  30. package/front_end/models/source_map_scopes/NamesResolver.ts +0 -8
  31. package/front_end/models/stack_trace/StackTraceImpl.ts +138 -47
  32. package/front_end/models/stack_trace/StackTraceModel.ts +117 -84
  33. package/front_end/models/stack_trace/Trie.ts +27 -0
  34. package/front_end/panels/accessibility/ARIAAttributesView.ts +6 -6
  35. package/front_end/panels/accessibility/AccessibilityAnnouncementRecordingView.ts +2 -2
  36. package/front_end/panels/accessibility/AccessibilityNodeView.ts +6 -9
  37. package/front_end/panels/accessibility/AccessibilitySidebarView.ts +83 -69
  38. package/front_end/panels/accessibility/AccessibilitySubPane.ts +24 -9
  39. package/front_end/panels/accessibility/SourceOrderView.ts +7 -3
  40. package/front_end/panels/accessibility/accessibilitySidebarView.css +5 -3
  41. package/front_end/panels/application/serviceWorkersView.css +1 -1
  42. package/front_end/panels/common/CommentThreadWidget.ts +30 -1
  43. package/front_end/panels/common/CommentsOverlayWidget.ts +42 -38
  44. package/front_end/panels/common/common-meta.ts +21 -9
  45. package/front_end/panels/console/ConsoleInsightTeaser.ts +25 -19
  46. package/front_end/panels/console/ConsoleViewMessage.ts +51 -21
  47. package/front_end/panels/console/console-meta.ts +1 -5
  48. package/front_end/panels/elements/DOMTreeWidget.ts +207 -586
  49. package/front_end/panels/elements/ElementsPanel.ts +1 -5
  50. package/front_end/panels/elements/StylePropertyTreeElement.ts +100 -5
  51. package/front_end/panels/lighthouse/LighthouseController.ts +9 -4
  52. package/front_end/panels/network/NetworkConfigView.ts +231 -187
  53. package/front_end/panels/network/RequestPayloadView.ts +22 -14
  54. package/front_end/panels/profiler/HeapDetachedElementsDataGrid.ts +0 -1
  55. package/front_end/panels/recorder/RecordingView.ts +42 -47
  56. package/front_end/panels/recorder/StepEditor.ts +1 -0
  57. package/front_end/panels/recorder/recordingView.css +4 -4
  58. package/front_end/panels/sources/CSSPlugin.ts +11 -0
  59. package/front_end/panels/sources/CoveragePlugin.ts +34 -23
  60. package/front_end/panels/sources/NavigatorView.ts +97 -52
  61. package/front_end/panels/sources/Plugin.ts +15 -1
  62. package/front_end/panels/sources/SourcesView.ts +47 -80
  63. package/front_end/panels/sources/TabbedEditorContainer.ts +27 -2
  64. package/front_end/panels/sources/UISourceCodeFrame.ts +11 -2
  65. package/front_end/panels/sources/navigatorTree.css +2 -2
  66. package/front_end/third_party/chromium/README.chromium +1 -1
  67. package/front_end/ui/comments/CommentAnchorResolver.ts +17 -0
  68. package/front_end/ui/components/suggestion_input/SuggestionInput.ts +10 -1
  69. package/front_end/ui/components/text_editor/AiCodeCompletionTeaser.ts +1 -1
  70. package/front_end/ui/components/text_editor/AiCodeGenerationTeaser.ts +2 -2
  71. package/front_end/ui/components/text_editor/aiCodeCompletionTeaser.css +0 -11
  72. package/front_end/ui/components/text_editor/aiCodeGenerationTeaser.css +0 -11
  73. package/front_end/ui/legacy/SoftDropDown.ts +17 -4
  74. package/front_end/ui/legacy/StackPane.ts +141 -0
  75. package/front_end/ui/legacy/TextPrompt.ts +37 -7
  76. package/front_end/ui/legacy/Toolbar.ts +7 -5
  77. package/front_end/ui/legacy/Widget.ts +3 -3
  78. package/front_end/ui/legacy/components/inline_editor/PositionAreaEditor.ts +85 -0
  79. package/front_end/ui/legacy/components/inline_editor/positionAreaEditor.css +61 -0
  80. package/front_end/ui/legacy/components/utils/Linkifier.ts +21 -45
  81. package/front_end/ui/legacy/inspectorCommon.css +11 -0
  82. package/front_end/ui/legacy/legacy.ts +2 -0
  83. package/front_end/ui/legacy/softContextMenu.css +2 -0
  84. package/front_end/ui/settings/ConsoleSettings.ts +7 -0
  85. package/front_end/ui/visual_logging/KnownContextValues.ts +10 -0
  86. package/package.json +1 -1
@@ -4,7 +4,7 @@
4
4
 
5
5
  import * as Host from '../../../core/host/host.js';
6
6
  import type * as LHModel from '../../lighthouse/lighthouse.js';
7
- import {LighthouseFormatter} from '../data_formatters/LighthouseFormatter.js';
7
+ import {type LighthouseCategoryArg, LighthouseFormatter} from '../data_formatters/LighthouseFormatter.js';
8
8
 
9
9
  import {
10
10
  type BaseToolCapability,
@@ -17,7 +17,7 @@ import {
17
17
 
18
18
  export interface RunLighthouseArgs extends ToolArgs {
19
19
  explanation: string;
20
- categoryId: LHModel.RunTypes.CategoryId;
20
+ categoryId: LighthouseCategoryArg;
21
21
  mode?: LHModel.RunTypes.RunMode;
22
22
  }
23
23
 
@@ -39,7 +39,9 @@ export class RunLighthouseTool implements
39
39
  },
40
40
  categoryId: {
41
41
  type: Host.AidaClient.ParametersTypes.STRING,
42
- description: 'Lighthouse category. E.g. "accessibility", "performance".',
42
+ // The experimental 'agentic-browsing' category is intentionally omitted from the prompt description so the agent does not invoke it unprompted. It is also excluded when 'all' is provided.
43
+ description:
44
+ 'Lighthouse category. Use "all" to run all categories, or specify a category: "accessibility", "performance", "best-practices", "seo".',
43
45
  nullable: false,
44
46
  },
45
47
  mode: {
@@ -64,16 +66,17 @@ export class RunLighthouseTool implements
64
66
  Promise<DataHandlerResult<{audits: string}>> {
65
67
  const mode = params.mode ?? 'snapshot';
66
68
  try {
69
+ // Passing undefined for categoryIds instructs the Lighthouse runner to audit all categories supported by the mode when isAIControlled is true.
67
70
  const report = await context.runLighthouse({
68
71
  mode,
69
- categoryIds: [params.categoryId],
72
+ categoryIds: params.categoryId === 'all' ? undefined : [params.categoryId],
70
73
  isAIControlled: true,
71
74
  });
72
75
  if (!report) {
73
76
  return {error: 'Error: Failed to record new audits.'};
74
77
  }
75
78
 
76
- const audits = new LighthouseFormatter().audits(report, params.categoryId);
79
+ const audits = new LighthouseFormatter().formatReport(report, params.categoryId);
77
80
  const isSnapshot = mode === 'snapshot';
78
81
  return {
79
82
  result: {audits},
@@ -75,6 +75,11 @@ export class CompilerScriptMapping implements DebuggerSourceMapping {
75
75
  ranges: SDK.SourceMapFunctionRanges.NamedFunctionRange[]): void {
76
76
  for (const sourceMap of this.#uiSourceCodeToSourceMaps.get(uiSourceCode)) {
77
77
  sourceMap.augmentWithScopes(uiSourceCode.url(), ranges);
78
+ // The scopes information changed, so stack traces of the script need to be re-translated.
79
+ const script = this.#sourceMapManager.clientForSourceMap(sourceMap);
80
+ if (script) {
81
+ void this.#debuggerWorkspaceBinding.updateLocations(script);
82
+ }
78
83
  }
79
84
  }
80
85
 
@@ -96,6 +101,11 @@ export class CompilerScriptMapping implements DebuggerSourceMapping {
96
101
  }
97
102
  }
98
103
 
104
+ /** @returns whether `uiSourceCode` is a placeholder for a script whose source map is still loading. */
105
+ isStubUISourceCode(uiSourceCode: Workspace.UISourceCode.UISourceCode): boolean {
106
+ return uiSourceCode.project() === this.#stubProject;
107
+ }
108
+
99
109
  getLocationRangesForSameSourceLocation(rawLocation: SDK.DebuggerModel.Location): SDK.DebuggerModel.LocationRange[] {
100
110
  const debuggerModel = rawLocation.debuggerModel;
101
111
  const script = rawLocation.script();
@@ -322,60 +332,92 @@ export class CompilerScriptMapping implements DebuggerSourceMapping {
322
332
  return new Workspace.UISourceCode.UIFunctionBounds(uiSourceCode, range, name);
323
333
  }
324
334
 
325
- async translateRawFramesStep(
326
- rawFrames: StackTraceImpl.Trie.RawFrame[],
327
- translatedFrames: Awaited<ReturnType<StackTraceImpl.StackTraceModel.TranslateRawFrames>>): Promise<boolean> {
328
- const frame = rawFrames[0];
329
- if (StackTraceImpl.Trie.isBuiltinFrame(frame)) {
330
- return false;
335
+ /**
336
+ * Translates a raw frame using the "scopes" information of its script's source map. Frames of compiler helpers
337
+ * are dropped (see {@link SDK.SourceMapScopesInfo.GeneratedFrameKind}).
338
+ *
339
+ * Outlined frames are merged at read time by the stack_trace model (see `consolidate`). The function keys
340
+ * tell it which authored function the top and bottom frames of a translation belong to. A frame at an
341
+ * unmapped position still gets its keys from the generated ranges; it shows the generated location, named
342
+ * after the authored function.
343
+ *
344
+ * @returns null if the raw frame can't be translated via "scopes" information, e.g. because the script doesn't
345
+ * have a source map (with scopes information), the source map is still loading, or neither mappings nor
346
+ * generated ranges know the position. It's then left to the default mapping.
347
+ */
348
+ async translateRawFrame(rawFrame: StackTraceImpl.Trie.RawFrame):
349
+ Promise<StackTraceImpl.StackTraceModel.TranslatedRawFrame|null> {
350
+ const translation = await this.#scopesTranslation(rawFrame);
351
+ if (!translation) {
352
+ return null;
353
+ }
354
+ if (translation.kind === SDK.SourceMapScopesInfo.GeneratedFrameKind.HIDDEN) {
355
+ // Compiler helpers don't represent any authored code.
356
+ return {kind: StackTraceImpl.Trie.FrameKind.HIDDEN, frames: []};
357
+ }
358
+ const {frames} = translation;
359
+ if (!frames.length) {
360
+ return null;
331
361
  }
362
+ return {
363
+ kind: translation.kind === SDK.SourceMapScopesInfo.GeneratedFrameKind.OUTLINED ?
364
+ StackTraceImpl.Trie.FrameKind.OUTLINED :
365
+ StackTraceImpl.Trie.FrameKind.VISIBLE,
366
+ frames: await this.#toUIFrames(translation, rawFrame),
367
+ functionKeys: {top: functionKey(frames[0]), bottom: functionKey(frames[frames.length - 1])},
368
+ };
369
+ }
332
370
 
333
- const sourceMapWithScopeInfoForFrame = async(rawFrame: StackTraceImpl.Trie.RawFrame):
334
- Promise<{sourceMap: SDK.SourceMap.SourceMap, script: SDK.Script.Script}|null> => {
335
- const script = this.#debuggerModel.scriptForId(rawFrame.scriptId ?? '');
336
- if (!script || this.#stubUISourceCodes.has(script)) {
337
- // Use fallback while source map is being loaded.
338
- return null;
339
- }
371
+ /** The raw translation of `rawFrame` by the "scopes" information of its script's source map, if any. */
372
+ async #scopesTranslation(rawFrame: StackTraceImpl.Trie.RawFrame): Promise<ScopesTranslation|null> {
373
+ if (StackTraceImpl.Trie.isBuiltinFrame(rawFrame)) {
374
+ return null;
375
+ }
340
376
 
341
- const sourceMap = script.sourceMap();
342
- await sourceMap?.waitForScopeInfo();
343
- return sourceMap?.hasScopeInfo() ? {sourceMap, script} : null;
344
- };
377
+ const script = this.#debuggerModel.scriptForId(rawFrame.scriptId ?? '');
378
+ if (!script || this.#stubUISourceCodes.has(script)) {
379
+ // Use fallback while source map is being loaded.
380
+ return null;
381
+ }
345
382
 
346
- const sourceMapAndScript = await sourceMapWithScopeInfoForFrame(frame);
347
- if (!sourceMapAndScript) {
348
- return false;
383
+ const sourceMap = script.sourceMap();
384
+ await sourceMap?.waitForScopeInfo();
385
+ if (!sourceMap?.hasScopeInfo()) {
386
+ return null;
349
387
  }
350
- const {sourceMap, script} = sourceMapAndScript;
351
- const {lineNumber, columnNumber} = script.rawLocationToRelativeLocation(frame);
352
388
 
353
- if (!sourceMap.isOutlinedFrame(lineNumber, columnNumber)) {
354
- const frames = sourceMap.translateCallSite(lineNumber, columnNumber);
355
- if (!frames.length) {
356
- return false;
357
- }
389
+ const {lineNumber, columnNumber} = script.rawLocationToRelativeLocation(rawFrame);
390
+ const translation = sourceMap.translateRawFrame(lineNumber, columnNumber);
391
+ return translation ? {...translation, sourceMap, script} : null;
392
+ }
358
393
 
359
- rawFrames.shift();
360
- const result: typeof translatedFrames[0] = [];
361
- translatedFrames.push(result);
362
-
363
- const project = this.#sourceMapToProject.get(sourceMap);
364
- for (const frame of frames) {
365
- // Switch out url for UISourceCode where we have it.
366
- const uiSourceCode = frame.url ? project?.uiSourceCodeForURL(frame.url) : undefined;
367
- result.push({
368
- ...frame,
369
- url: uiSourceCode ? undefined : frame.url,
370
- uiSourceCode: uiSourceCode ?? undefined,
371
- });
394
+ /**
395
+ * Switch out url for UISourceCode where we have it. A top frame without position (unmapped generated position)
396
+ * gets the generated location of `rawFrame`.
397
+ */
398
+ async #toUIFrames({sourceMap, script, frames}: ScopesTranslation, rawFrame: StackTraceImpl.Trie.RawFrame):
399
+ Promise<StackTraceImpl.StackTraceModel.TranslatedUIFrame[]> {
400
+ const project = this.#sourceMapToProject.get(sourceMap);
401
+ return await Promise.all(frames.map(async ({line, column, name, url}) => {
402
+ if (line === undefined || column === undefined) {
403
+ return {...await this.#generatedUIFrame(script, rawFrame), name};
372
404
  }
405
+ const uiSourceCode = url ? project?.uiSourceCodeForURL(url) : undefined;
406
+ return {line, column, name, url: uiSourceCode ? undefined : url, uiSourceCode: uiSourceCode ?? undefined};
407
+ }));
408
+ }
373
409
 
374
- return true;
375
- }
376
-
377
- // TODO(crbug.com/433162438): Consolidate outlined frames.
378
- return false;
410
+ /** The location of `rawFrame` in terms of the generated `script`, as the non-compiler mappings would show it. */
411
+ async #generatedUIFrame(script: SDK.Script.Script, rawFrame: StackTraceImpl.Trie.RawFrame):
412
+ Promise<StackTraceImpl.StackTraceModel.TranslatedUIFrame> {
413
+ const rawLocation = this.#debuggerModel.createRawLocation(script, rawFrame.lineNumber, rawFrame.columnNumber);
414
+ // Goes through the DebuggerWorkspaceBinding so that e.g. inline scripts resolve to their document.
415
+ const uiLocation = await this.#debuggerWorkspaceBinding.rawLocationToUILocation(rawLocation);
416
+ if (uiLocation) {
417
+ const {uiSourceCode, lineNumber: line, columnNumber} = uiLocation;
418
+ return {uiSourceCode, line, column: columnNumber ?? -1};
419
+ }
420
+ return {url: rawFrame.url, line: rawFrame.lineNumber, column: rawFrame.columnNumber};
379
421
  }
380
422
 
381
423
  /**
@@ -594,3 +636,18 @@ export class CompilerScriptMapping implements DebuggerSourceMapping {
594
636
  this.#stubProject.dispose();
595
637
  }
596
638
  }
639
+
640
+ type ScopesTranslation = SDK.SourceMapScopesInfo.RawFrameTranslation&{
641
+ sourceMap: SDK.SourceMap.SourceMap,
642
+ script: SDK.Script.Script,
643
+ };
644
+
645
+ /**
646
+ * Identifies the authored function of a translated frame. The frames can originate from different source maps
647
+ * (bundles), so we can't compare original scopes directly. The start position tells apart anonymous and
648
+ * same-named functions in one file. All top-level code of a file shares a key.
649
+ */
650
+ function functionKey(frame: SDK.SourceMapScopesInfo.TranslatedFrame): string {
651
+ const start = frame.functionStart ? `${frame.functionStart.line}:${frame.functionStart.column}` : '';
652
+ return `${frame.url ?? ''}\n${frame.name ?? ''}\n${start}`;
653
+ }
@@ -12,7 +12,7 @@ import * as TextUtils from '../../core/text_utils/text_utils.js';
12
12
  import * as Protocol from '../../generated/protocol.js';
13
13
  import * as StackTrace from '../stack_trace/stack_trace.js';
14
14
  // eslint-disable-next-line @devtools/es-modules-import
15
- import type * as StackTraceImpl from '../stack_trace/stack_trace_impl.js';
15
+ import * as StackTraceImpl from '../stack_trace/stack_trace_impl.js';
16
16
  import * as Workspace from '../workspace/workspace.js';
17
17
 
18
18
  import {ContentProviderBasedProject} from './ContentProviderBasedProject.js';
@@ -166,7 +166,8 @@ class SourceScopeRemoteObject extends SDK.RemoteObject.RemoteObjectImpl {
166
166
  }
167
167
 
168
168
  const properties = [];
169
- const namespaces: Record<string, SDK.RemoteObject.RemoteObject> = {};
169
+ const namespaces: Record<string, SDK.RemoteObject.RemoteObject> =
170
+ Object.create(null) as Record<string, SDK.RemoteObject.RemoteObject>;
170
171
 
171
172
  function makeProperty(name: string, obj: SDK.RemoteObject.RemoteObject): SDK.RemoteObject.RemoteObjectProperty {
172
173
  return new SDK.RemoteObject.RemoteObjectProperty(
@@ -190,7 +191,7 @@ class SourceScopeRemoteObject extends SDK.RemoteObject.RemoteObjectImpl {
190
191
  const nestedName = variable.nestedName[index];
191
192
  let child: NamespaceObject|SDK.RemoteObject.RemoteObject = parent[nestedName];
192
193
  if (!child) {
193
- child = new NamespaceObject({});
194
+ child = new NamespaceObject(Object.create(null));
194
195
  parent[nestedName] = child;
195
196
  }
196
197
  parent = child.value;
@@ -561,6 +562,9 @@ export class DebuggerLanguagePluginManager implements
561
562
  // new instance of the #plugin added before we remove
562
563
  // the previous instance.
563
564
  this.parsedScriptSource({data: script});
565
+ if (!this.hasPluginForScript(script)) {
566
+ void this.#debuggerWorkspaceBinding.updateLocations(script);
567
+ }
564
568
  }
565
569
  }
566
570
 
@@ -731,25 +735,24 @@ export class DebuggerLanguagePluginManager implements
731
735
  return ranges;
732
736
  }
733
737
 
734
- async translateRawFramesStep(
735
- rawFrames: StackTraceImpl.Trie.RawFrame[],
736
- translatedFrames: Awaited<ReturnType<StackTraceImpl.StackTraceModel.TranslateRawFrames>>,
737
- target: SDK.Target.Target): Promise<boolean> {
738
- const frame = rawFrames[0];
738
+ /**
739
+ * Translates a raw frame via the language plugin responsible for its script.
740
+ *
741
+ * @returns null if no plugin is responsible for the frame. Otherwise the frame is translated, either
742
+ * successfully, or identity mapped with the "missing debug info details" attached.
743
+ */
744
+ async translateRawFrame(frame: StackTraceImpl.Trie.RawFrame,
745
+ target: SDK.Target.Target): Promise<StackTraceImpl.StackTraceModel.TranslatedRawFrame|null> {
739
746
  const script = target.model(SDK.DebuggerModel.DebuggerModel)?.scriptForId(frame.scriptId ?? '');
740
747
  if (!script) {
741
- return false;
748
+ return null;
742
749
  }
743
750
 
744
751
  const functionInfo = await this.getFunctionInfo(script, frame);
745
752
  if (!functionInfo) {
746
- return false;
753
+ return null;
747
754
  }
748
755
 
749
- // The plugin is responsible for translating this frame. The only question is whether it was successful,
750
- // or if we identity map the raw frame and attach the "missing debug info details".
751
- rawFrames.shift();
752
-
753
756
  if ('frames' in functionInfo && functionInfo.frames.length) {
754
757
  const framePromises = functionInfo.frames.map(async ({name}, index) => {
755
758
  const rawLocation = new SDK.DebuggerModel.Location(
@@ -758,8 +761,7 @@ export class DebuggerLanguagePluginManager implements
758
761
  return translatedFromUILocation(uiLocation, name, frame);
759
762
  });
760
763
 
761
- translatedFrames.push(await Promise.all(framePromises));
762
- return true;
764
+ return {kind: StackTraceImpl.Trie.FrameKind.VISIBLE, frames: await Promise.all(framePromises), unmapped: false};
763
765
  }
764
766
 
765
767
  // Translate the location only. We go through via "DebuggerWorkspaceBinding". It'll still try the plugin
@@ -768,28 +770,18 @@ export class DebuggerLanguagePluginManager implements
768
770
  new SDK.DebuggerModel.Location(script.debuggerModel, script.scriptId, frame.lineNumber, frame.columnNumber));
769
771
  const mappedFrame = translatedFromUILocation(uiLocation, frame.functionName, frame);
770
772
 
771
- if ('missingSymbolFiles' in functionInfo && functionInfo.missingSymbolFiles.length) {
772
- translatedFrames.push([{
773
- ...mappedFrame,
774
- missingDebugInfo: {
773
+ const missingDebugInfo: StackTrace.StackTrace.MissingDebugInfo =
774
+ 'missingSymbolFiles' in functionInfo && functionInfo.missingSymbolFiles.length ?
775
+ {
775
776
  type: StackTrace.StackTrace.MissingDebugInfoType.PARTIAL_INFO,
776
777
  missingDebugFiles: functionInfo.missingSymbolFiles,
777
- },
778
- }]);
779
- } else {
780
- translatedFrames.push([{
781
- ...mappedFrame,
782
- missingDebugInfo: {
783
- type: StackTrace.StackTrace.MissingDebugInfoType.NO_INFO,
784
- },
785
- }]);
786
- }
787
-
788
- return true;
778
+ } :
779
+ {type: StackTrace.StackTrace.MissingDebugInfoType.NO_INFO};
780
+ return {kind: StackTraceImpl.Trie.FrameKind.VISIBLE, frames: [{...mappedFrame, missingDebugInfo}], unmapped: true};
789
781
 
790
782
  function translatedFromUILocation(
791
783
  uiLocation: Workspace.UISourceCode.UILocation|null, name: string|undefined,
792
- fallback: StackTraceImpl.Trie.RawFrame): (typeof translatedFrames)[number][number] {
784
+ fallback: StackTraceImpl.Trie.RawFrame): StackTraceImpl.StackTraceModel.TranslatedUIFrame {
793
785
  if (uiLocation) {
794
786
  return {
795
787
  uiSourceCode: uiLocation.uiSourceCode,
@@ -517,32 +517,29 @@ export class DebuggerWorkspaceBinding implements SDK.TargetManager.SDKModelObser
517
517
 
518
518
  async #translateRawFrames(frames: readonly StackTraceImpl.Trie.RawFrame[], target: SDK.Target.Target):
519
519
  ReturnType<StackTraceImpl.StackTraceModel.TranslateRawFrames> {
520
- const rawFrames = frames.slice(0);
521
- const translatedFrames: Awaited<ReturnType<StackTraceImpl.StackTraceModel.TranslateRawFrames>> = [];
522
- while (rawFrames.length) {
523
- await this.#translateRawFramesStep(rawFrames, translatedFrames, target);
524
- }
525
- return translatedFrames;
520
+ return await Promise.all(frames.map(frame => this.#translateRawFrame(frame, target)));
526
521
  }
527
522
 
528
- async #translateRawFramesStep(
529
- rawFrames: StackTraceImpl.Trie.RawFrame[],
530
- translatedFrames: Awaited<ReturnType<StackTraceImpl.StackTraceModel.TranslateRawFrames>>,
531
- target: SDK.Target.Target): Promise<void> {
532
- if (await this.pluginManager.translateRawFramesStep(rawFrames, translatedFrames, target)) {
533
- return;
523
+ /** Translates a single raw frame: language plugins first, then the script mappings of the frame's debugger model. */
524
+ async #translateRawFrame(frame: StackTraceImpl.Trie.RawFrame,
525
+ target: SDK.Target.Target): Promise<StackTraceImpl.StackTraceModel.TranslatedRawFrame> {
526
+ const pluginTranslation = await this.pluginManager.translateRawFrame(frame, target);
527
+ if (pluginTranslation) {
528
+ return pluginTranslation;
534
529
  }
535
530
 
536
531
  const modelData =
537
532
  this.#debuggerModelToData.get(target.model(SDK.DebuggerModel.DebuggerModel) as SDK.DebuggerModel.DebuggerModel);
538
533
  if (modelData) {
539
- await modelData.translateRawFramesStep(rawFrames, translatedFrames);
540
- return;
534
+ return await modelData.translateRawFrame(frame);
541
535
  }
542
536
 
543
- const frame = rawFrames.shift() as StackTraceImpl.Trie.RawFrame;
544
537
  const {url, lineNumber, columnNumber, functionName} = frame;
545
- translatedFrames.push([{url, line: lineNumber, column: columnNumber, name: functionName}]);
538
+ return {
539
+ kind: StackTraceImpl.Trie.FrameKind.VISIBLE,
540
+ frames: [{url, line: lineNumber, column: columnNumber, name: functionName}],
541
+ unmapped: true,
542
+ };
546
543
  }
547
544
  }
548
545
 
@@ -592,8 +589,12 @@ class ModelData {
592
589
  }
593
590
 
594
591
  rawLocationToUILocation(rawLocation: SDK.DebuggerModel.Location): Workspace.UISourceCode.UILocation|null {
595
- let uiLocation = this.compilerMapping.rawLocationToUILocation(rawLocation);
596
- uiLocation = uiLocation || this.#resourceScriptMapping.rawLocationToUILocation(rawLocation);
592
+ return this.compilerMapping.rawLocationToUILocation(rawLocation) ||
593
+ this.#nonCompilerRawLocationToUILocation(rawLocation);
594
+ }
595
+
596
+ #nonCompilerRawLocationToUILocation(rawLocation: SDK.DebuggerModel.Location): Workspace.UISourceCode.UILocation|null {
597
+ let uiLocation = this.#resourceScriptMapping.rawLocationToUILocation(rawLocation);
597
598
  uiLocation = uiLocation || this.#resourceMapping.jsLocationToUILocation(rawLocation);
598
599
  uiLocation = uiLocation || this.#defaultMapping.rawLocationToUILocation(rawLocation);
599
600
  return uiLocation;
@@ -646,37 +647,30 @@ class ModelData {
646
647
  return scope;
647
648
  }
648
649
 
649
- async translateRawFramesStep(
650
- rawFrames: StackTraceImpl.Trie.RawFrame[],
651
- translatedFrames: Awaited<ReturnType<StackTraceImpl.StackTraceModel.TranslateRawFrames>>): Promise<void> {
652
- if (!await this.compilerMapping.translateRawFramesStep(rawFrames, translatedFrames)) {
653
- this.#defaultTranslateRawFramesStep(rawFrames, translatedFrames);
654
- }
650
+ async translateRawFrame(frame: StackTraceImpl.Trie.RawFrame):
651
+ Promise<StackTraceImpl.StackTraceModel.TranslatedRawFrame> {
652
+ return await this.compilerMapping.translateRawFrame(frame) ?? this.#defaultTranslateRawFrame(frame);
655
653
  }
656
654
 
657
- /** The default implementation translates one frame at a time and only translates the location, but not the function name. */
658
- #defaultTranslateRawFramesStep(
659
- rawFrames: StackTraceImpl.Trie.RawFrame[],
660
- translatedFrames: Awaited<ReturnType<StackTraceImpl.StackTraceModel.TranslateRawFrames>>): void {
661
- const frame = rawFrames.shift() as StackTraceImpl.Trie.RawFrame;
655
+ /** The default translation only translates the location, but not the function name. */
656
+ #defaultTranslateRawFrame(frame: StackTraceImpl.Trie.RawFrame): StackTraceImpl.StackTraceModel.TranslatedRawFrame {
662
657
  const {scriptId, url, lineNumber, columnNumber, functionName} = frame;
663
658
  const rawLocation = scriptId ? this.#debuggerModel.createRawLocationByScriptId(scriptId, lineNumber, columnNumber) :
664
659
  url ? this.#debuggerModel.createRawLocationByURL(url, lineNumber, columnNumber) :
665
660
  null;
666
- if (rawLocation) {
667
- const uiLocation = this.rawLocationToUILocation(rawLocation);
668
- if (uiLocation) {
669
- translatedFrames.push([{
661
+ const mapped = rawLocation && this.compilerMapping.rawLocationToUILocation(rawLocation);
662
+ // A stub UISourceCode shows the generated script while its source map is loading.
663
+ const unmapped = !mapped || this.compilerMapping.isStubUISourceCode(mapped.uiSourceCode);
664
+ const uiLocation = mapped || (rawLocation && this.#nonCompilerRawLocationToUILocation(rawLocation));
665
+ const translatedFrame: StackTraceImpl.StackTraceModel.TranslatedUIFrame = uiLocation ?
666
+ {
670
667
  uiSourceCode: uiLocation.uiSourceCode,
671
668
  name: functionName,
672
669
  line: uiLocation.lineNumber,
673
670
  column: uiLocation.columnNumber ?? -1,
674
- }]);
675
- return;
676
- }
677
- }
678
-
679
- translatedFrames.push([{url, line: lineNumber, column: columnNumber, name: functionName}]);
671
+ } :
672
+ {url, line: lineNumber, column: columnNumber, name: functionName};
673
+ return {kind: StackTraceImpl.Trie.FrameKind.VISIBLE, frames: [translatedFrame], unmapped};
680
674
  }
681
675
 
682
676
  getMappedLines(uiSourceCode: Workspace.UISourceCode.UISourceCode): Set<number>|null {
@@ -176,6 +176,9 @@ export class StyleFile implements TextUtils.ContentProvider.ContentProvider {
176
176
  this.uiSourceCode = this.#project.createUISourceCode(url, header.contentType());
177
177
  uiSourceCodeToStyleMap.set(this.uiSourceCode, this);
178
178
  NetworkProject.setInitialFrameAttribution(this.uiSourceCode, header.frameId);
179
+ if (header.hasSourceURL) {
180
+ NetworkProject.setSourceURLSynthesized(this.uiSourceCode);
181
+ }
179
182
  this.#project.addUISourceCodeWithProvider(this.uiSourceCode, this, metadata, 'text/css');
180
183
 
181
184
  this.#eventListeners = [
@@ -14,6 +14,13 @@ interface EventMessage {
14
14
  event: string;
15
15
  }
16
16
 
17
+ interface Message {
18
+ data: Response|EventMessage;
19
+ }
20
+ type ResultValidator<T> = (result: unknown) => result is T;
21
+
22
+ const isUndefined = (result: unknown): result is undefined => result === undefined;
23
+
17
24
  export class ExtensionEndpoint {
18
25
  private readonly port: Platform.HostRuntime.WorkerMessagePort;
19
26
  private nextRequestId = 0;
@@ -24,16 +31,30 @@ export class ExtensionEndpoint {
24
31
 
25
32
  constructor(port: Platform.HostRuntime.WorkerMessagePort) {
26
33
  this.port = port;
27
- this.port.addEventListener('message', (event: unknown) => this.onResponse(event as MessageEvent));
34
+ this.port.addEventListener('message', (event: unknown) => this.onResponse(event as Message));
28
35
  (this.port as {start?: () => void}).start?.();
29
36
  (this.port as {unref?: () => void}).unref?.();
30
37
  this.pendingRequests = new Map();
31
38
  }
32
39
 
33
- sendRequest<ReturnType>(method: string, parameters: unknown): Promise<ReturnType> {
34
- return new Promise((resolve, reject) => {
40
+ sendRequest(method: string, parameters: unknown): Promise<void>;
41
+ sendRequest<ReturnType>(method: string, parameters: unknown,
42
+ validate: ResultValidator<ReturnType>): Promise<ReturnType>;
43
+ sendRequest<ReturnType>(method: string, parameters: unknown,
44
+ validate: ResultValidator<ReturnType>|
45
+ ResultValidator<undefined> = isUndefined): Promise<ReturnType|void> {
46
+ return new Promise<ReturnType|void>((resolve, reject) => {
35
47
  const requestId = this.nextRequestId++;
36
- this.pendingRequests.set(requestId, {resolve: resolve as (arg: unknown) => void, reject});
48
+ this.pendingRequests.set(requestId, {
49
+ resolve: (result: unknown) => {
50
+ if (!validate(result)) {
51
+ reject(new Error(`Extension returned malformed ${method} result`));
52
+ return;
53
+ }
54
+ resolve(result);
55
+ },
56
+ reject,
57
+ });
37
58
  this.port.postMessage({requestId, method, parameters});
38
59
  });
39
60
  }
@@ -46,8 +67,8 @@ export class ExtensionEndpoint {
46
67
  this.port.close();
47
68
  }
48
69
 
49
- private onResponse(event: MessageEvent): void {
50
- const data = event.data as Response | EventMessage;
70
+ private onResponse(event: Message): void {
71
+ const data = event.data;
51
72
  if ('event' in data) {
52
73
  this.handleEvent(data);
53
74
  return;