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.
Files changed (74) hide show
  1. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/Base64.js +20 -2
  2. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/Gzip.js +11 -0
  3. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/Object.js +6 -1
  4. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/ParsedURL.js +3 -0
  5. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/ResourceType.js +6 -0
  6. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/Settings.js +18 -8
  7. package/build/node_modules/chrome-devtools-frontend/front_end/core/host/InspectorFrontendHostStub.js +3 -3
  8. package/build/node_modules/chrome-devtools-frontend/front_end/core/host/ResourceLoader.js +1 -1
  9. package/build/node_modules/chrome-devtools-frontend/front_end/core/host/UserMetrics.js +17 -1
  10. package/build/node_modules/chrome-devtools-frontend/front_end/core/platform/ArrayUtilities.js +10 -0
  11. package/build/node_modules/chrome-devtools-frontend/front_end/core/platform/StringUtilities.js +63 -12
  12. package/build/node_modules/chrome-devtools-frontend/front_end/core/protocol_client/CDPConnection.js +1 -0
  13. package/build/node_modules/chrome-devtools-frontend/front_end/core/protocol_client/InspectorBackend.js +4 -1
  14. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/CSSMatchedStyles.js +44 -9
  15. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/CSSMetadata.js +6 -6
  16. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/CSSModel.js +1 -1
  17. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/DOMModel.js +169 -12
  18. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/DebuggerModel.js +2 -1
  19. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/IsolateManager.js +6 -0
  20. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/NetworkManager.js +18 -4
  21. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/NetworkRequest.js +7 -21
  22. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/OverlayModel.js +17 -5
  23. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/RehydratingConnection.js +5 -1
  24. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/ResourceTreeModel.js +8 -5
  25. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/SourceMap.js +14 -2
  26. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/SourceMapManager.js +1 -1
  27. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/SourceMapScopesInfo.js +11 -4
  28. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/Target.js +3 -1
  29. package/build/node_modules/chrome-devtools-frontend/front_end/generated/ARIAProperties.js +1 -1
  30. package/build/node_modules/chrome-devtools-frontend/front_end/generated/Deprecation.js +1 -16
  31. package/build/node_modules/chrome-devtools-frontend/front_end/generated/InspectorBackendCommands.js +35 -14
  32. package/build/node_modules/chrome-devtools-frontend/front_end/generated/SupportedCSSProperties.js +197 -101
  33. package/build/node_modules/chrome-devtools-frontend/front_end/models/ai_assistance/data_formatters/NetworkRequestFormatter.js +2 -1
  34. package/build/node_modules/chrome-devtools-frontend/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.js +10 -16
  35. package/build/node_modules/chrome-devtools-frontend/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.js +97 -26
  36. package/build/node_modules/chrome-devtools-frontend/front_end/models/ai_assistance/performance/AICallTree.js +35 -0
  37. package/build/node_modules/chrome-devtools-frontend/front_end/models/annotations/AnnotationRepository.js +163 -0
  38. package/build/node_modules/chrome-devtools-frontend/front_end/models/annotations/AnnotationType.js +10 -0
  39. package/build/node_modules/chrome-devtools-frontend/front_end/models/annotations/annotations.js +5 -0
  40. package/build/node_modules/chrome-devtools-frontend/front_end/models/bindings/CompilerScriptMapping.js +5 -3
  41. package/build/node_modules/chrome-devtools-frontend/front_end/models/bindings/DebuggerWorkspaceBinding.js +7 -3
  42. package/build/node_modules/chrome-devtools-frontend/front_end/models/emulation/DeviceModeModel.js +1 -1
  43. package/build/node_modules/chrome-devtools-frontend/front_end/models/emulation/EmulatedDevices.js +14 -0
  44. package/build/node_modules/chrome-devtools-frontend/front_end/models/formatter/FormatterWorkerPool.js +8 -5
  45. package/build/node_modules/chrome-devtools-frontend/front_end/models/greendev/Prototypes.js +33 -0
  46. package/build/node_modules/chrome-devtools-frontend/front_end/models/greendev/greendev.js +4 -0
  47. package/build/node_modules/chrome-devtools-frontend/front_end/models/stack_trace/StackTraceImpl.js +70 -1
  48. package/build/node_modules/chrome-devtools-frontend/front_end/models/stack_trace/StackTraceModel.js +82 -30
  49. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/EventsSerializer.js +7 -2
  50. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/LanternComputationData.js +2 -2
  51. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/Processor.js +18 -19
  52. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/Styles.js +12 -4
  53. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/extras/Initiators.js +46 -0
  54. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/extras/TraceTree.js +4 -3
  55. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/extras/extras.js +1 -0
  56. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/handlers/LargestImagePaintHandler.js +2 -2
  57. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/handlers/LayoutShiftsHandler.js +1 -1
  58. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/handlers/MetaHandler.js +6 -0
  59. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/handlers/NetworkRequestsHandler.js +10 -1
  60. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/handlers/PageLoadMetricsHandler.js +44 -27
  61. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/helpers/Timing.js +9 -2
  62. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/Common.js +1 -6
  63. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/LCPBreakdown.js +2 -2
  64. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/LCPDiscovery.js +2 -4
  65. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/NetworkDependencyTree.js +3 -2
  66. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/RenderBlocking.js +1 -1
  67. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/types/TraceEvents.js +30 -11
  68. package/build/node_modules/chrome-devtools-frontend/front_end/third_party/source-map-scopes-codec/package/src/decode/decode.js +28 -13
  69. package/build/node_modules/chrome-devtools-frontend/front_end/third_party/source-map-scopes-codec/package/src/encode/encoder.js +1 -1
  70. package/build/node_modules/chrome-devtools-frontend/front_end/third_party/source-map-scopes-codec/package/src/scopes.js +4 -0
  71. package/build/src/tools/chatgpt-web.js +68 -49
  72. package/build/src/tools/gemini-web.js +66 -22
  73. package/build/src/tools/pages.js +0 -1
  74. package/package.json +1 -1
@@ -141,6 +141,10 @@ export class DebuggerWorkspaceBinding {
141
141
  const model = target.model(StackTraceImpl.StackTraceModel.StackTraceModel);
142
142
  return await model.createFromProtocolRuntime(stackTrace, this.#translateRawFrames.bind(this));
143
143
  }
144
+ async createStackTraceFromDebuggerPaused(pausedDetails, target) {
145
+ const model = target.model(StackTraceImpl.StackTraceModel.StackTraceModel);
146
+ return await model.createFromDebuggerPaused(pausedDetails, this.#translateRawFrames.bind(this));
147
+ }
144
148
  async createLiveLocation(rawLocation, updateDelegate, locationPool) {
145
149
  const modelData = this.#debuggerModelToData.get(rawLocation.debuggerModel);
146
150
  if (!modelData) {
@@ -382,7 +386,7 @@ export class DebuggerWorkspaceBinding {
382
386
  }
383
387
  const modelData = this.#debuggerModelToData.get(target.model(SDK.DebuggerModel.DebuggerModel));
384
388
  if (modelData) {
385
- modelData.translateRawFramesStep(rawFrames, translatedFrames);
389
+ await modelData.translateRawFramesStep(rawFrames, translatedFrames);
386
390
  return;
387
391
  }
388
392
  const frame = rawFrames.shift();
@@ -473,8 +477,8 @@ class ModelData {
473
477
  scope = scope || await this.#resourceMapping.functionBoundsAtRawLocation(rawLocation);
474
478
  return scope;
475
479
  }
476
- translateRawFramesStep(rawFrames, translatedFrames) {
477
- if (!this.compilerMapping.translateRawFramesStep(rawFrames, translatedFrames)) {
480
+ async translateRawFramesStep(rawFrames, translatedFrames) {
481
+ if (!await this.compilerMapping.translateRawFramesStep(rawFrames, translatedFrames)) {
478
482
  this.#defaultTranslateRawFramesStep(rawFrames, translatedFrames);
479
483
  }
480
484
  }
@@ -107,7 +107,7 @@ export class DeviceModeModel extends Common.ObjectWrapper.ObjectWrapper {
107
107
  this.#preferredSize = new Geometry.Size(1, 1);
108
108
  this.#initialized = false;
109
109
  this.#appliedDeviceSize = new Geometry.Size(1, 1);
110
- this.#appliedDeviceScaleFactor = window.devicePixelRatio;
110
+ this.#appliedDeviceScaleFactor = globalThis.devicePixelRatio;
111
111
  this.#appliedUserAgentType = "Desktop" /* UA.DESKTOP */;
112
112
  this.#scaleSetting = Common.Settings.Settings.instance().createSetting('emulation.device-scale', 1);
113
113
  // We've used to allow zero before.
@@ -550,6 +550,7 @@ const emulatedDevices = [
550
550
  },
551
551
  'capabilities': ['touch', 'mobile'],
552
552
  'user-agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 18_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.5 Mobile/15E148 Safari/604.1',
553
+ 'user-agent-metadata': { 'platform': 'iOS', 'platformVersion': '18.5', 'architecture': '', 'model': 'iPhone', 'mobile': true },
553
554
  'type': 'phone',
554
555
  },
555
556
  {
@@ -569,6 +570,7 @@ const emulatedDevices = [
569
570
  },
570
571
  'capabilities': ['touch', 'mobile'],
571
572
  'user-agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 18_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.5 Mobile/15E148 Safari/604.1',
573
+ 'user-agent-metadata': { 'platform': 'iOS', 'platformVersion': '18.5', 'architecture': '', 'model': 'iPhone', 'mobile': true },
572
574
  'type': 'phone',
573
575
  },
574
576
  {
@@ -588,6 +590,7 @@ const emulatedDevices = [
588
590
  },
589
591
  'capabilities': ['touch', 'mobile'],
590
592
  'user-agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 18_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.5 Mobile/15E148 Safari/604.1',
593
+ 'user-agent-metadata': { 'platform': 'iOS', 'platformVersion': '18.5', 'architecture': '', 'model': 'iPhone', 'mobile': true },
591
594
  'type': 'phone',
592
595
  },
593
596
  {
@@ -607,6 +610,7 @@ const emulatedDevices = [
607
610
  },
608
611
  'capabilities': ['touch', 'mobile'],
609
612
  'user-agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 18_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.5 Mobile/15E148 Safari/604.1',
613
+ 'user-agent-metadata': { 'platform': 'iOS', 'platformVersion': '18.5', 'architecture': '', 'model': 'iPhone', 'mobile': true },
610
614
  'type': 'phone',
611
615
  },
612
616
  {
@@ -706,6 +710,7 @@ const emulatedDevices = [
706
710
  },
707
711
  'capabilities': ['touch', 'mobile'],
708
712
  'user-agent': 'Mozilla/5.0 (iPad; CPU OS 18_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.5 Mobile/15E148 Safari/604.1',
713
+ 'user-agent-metadata': { 'platform': 'iOS', 'platformVersion': '18.5', 'architecture': '', 'model': 'iPad', 'mobile': true },
709
714
  'type': 'tablet',
710
715
  },
711
716
  {
@@ -725,6 +730,7 @@ const emulatedDevices = [
725
730
  },
726
731
  'capabilities': ['touch', 'mobile'],
727
732
  'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.5 Safari/605.1.15',
733
+ 'user-agent-metadata': { 'platform': 'iOS', 'platformVersion': '18.5', 'architecture': '', 'model': 'iPad', 'mobile': true },
728
734
  'type': 'tablet',
729
735
  },
730
736
  {
@@ -744,6 +750,7 @@ const emulatedDevices = [
744
750
  },
745
751
  'capabilities': ['touch', 'mobile'],
746
752
  'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.5 Safari/605.1.15',
753
+ 'user-agent-metadata': { 'platform': 'iOS', 'platformVersion': '18.5', 'architecture': '', 'model': 'iPad', 'mobile': true },
747
754
  'type': 'tablet',
748
755
  },
749
756
  {
@@ -982,6 +989,7 @@ const emulatedDevices = [
982
989
  },
983
990
  'capabilities': ['touch', 'mobile'],
984
991
  'user-agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 7_1_2 like Mac OS X) AppleWebKit/537.51.2 (KHTML, like Gecko) Version/7.0 Mobile/11D257 Safari/9537.53',
992
+ 'user-agent-metadata': { 'platform': 'iOS', 'platformVersion': '7.1.2', 'architecture': '', 'model': 'iPhone', 'mobile': true },
985
993
  'type': 'phone',
986
994
  },
987
995
  {
@@ -1009,6 +1017,7 @@ const emulatedDevices = [
1009
1017
  },
1010
1018
  'capabilities': ['touch', 'mobile'],
1011
1019
  'user-agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_1 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/10.0 Mobile/14E304 Safari/602.1',
1020
+ 'user-agent-metadata': { 'platform': 'iOS', 'platformVersion': '10.3.1', 'architecture': '', 'model': 'iPhone', 'mobile': true },
1012
1021
  'type': 'phone',
1013
1022
  },
1014
1023
  {
@@ -1036,6 +1045,7 @@ const emulatedDevices = [
1036
1045
  },
1037
1046
  'capabilities': ['touch', 'mobile'],
1038
1047
  'user-agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1',
1048
+ 'user-agent-metadata': { 'platform': 'iOS', 'platformVersion': '13.2.3', 'architecture': '', 'model': 'iPhone', 'mobile': true },
1039
1049
  'type': 'phone',
1040
1050
  },
1041
1051
  {
@@ -1063,6 +1073,7 @@ const emulatedDevices = [
1063
1073
  },
1064
1074
  'capabilities': ['touch', 'mobile'],
1065
1075
  'user-agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1',
1076
+ 'user-agent-metadata': { 'platform': 'iOS', 'platformVersion': '13.2.3', 'architecture': '', 'model': 'iPhone', 'mobile': true },
1066
1077
  'type': 'phone',
1067
1078
  },
1068
1079
  {
@@ -1076,6 +1087,7 @@ const emulatedDevices = [
1076
1087
  },
1077
1088
  'capabilities': ['touch', 'mobile'],
1078
1089
  'user-agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1',
1090
+ 'user-agent-metadata': { 'platform': 'iOS', 'platformVersion': '13.2.3', 'architecture': '', 'model': 'iPhone', 'mobile': true },
1079
1091
  'type': 'phone',
1080
1092
  },
1081
1093
  {
@@ -1499,6 +1511,7 @@ const emulatedDevices = [
1499
1511
  },
1500
1512
  'capabilities': ['touch', 'mobile'],
1501
1513
  'user-agent': 'Mozilla/5.0 (iPad; CPU OS 11_0 like Mac OS X) AppleWebKit/604.1.34 (KHTML, like Gecko) Version/11.0 Mobile/15A5341f Safari/604.1',
1514
+ 'user-agent-metadata': { 'platform': 'iOS', 'platformVersion': '11.0', 'architecture': '', 'model': 'iPad', 'mobile': true },
1502
1515
  'type': 'tablet',
1503
1516
  },
1504
1517
  {
@@ -1512,6 +1525,7 @@ const emulatedDevices = [
1512
1525
  },
1513
1526
  'capabilities': ['touch', 'mobile'],
1514
1527
  'user-agent': 'Mozilla/5.0 (iPad; CPU OS 11_0 like Mac OS X) AppleWebKit/604.1.34 (KHTML, like Gecko) Version/11.0 Mobile/15A5341f Safari/604.1',
1528
+ 'user-agent-metadata': { 'platform': 'iOS', 'platformVersion': '11.0', 'architecture': '', 'model': 'iPad', 'mobile': true },
1515
1529
  'type': 'tablet',
1516
1530
  },
1517
1531
  {
@@ -6,13 +6,16 @@ let formatterWorkerPoolInstance;
6
6
  export class FormatterWorkerPool {
7
7
  taskQueue;
8
8
  workerTasks;
9
- constructor() {
9
+ entrypointURL;
10
+ constructor(entrypointURL) {
10
11
  this.taskQueue = [];
11
12
  this.workerTasks = new Map();
13
+ this.entrypointURL =
14
+ entrypointURL ?? import.meta.resolve('../../entrypoints/formatter_worker/formatter_worker-entrypoint.js');
12
15
  }
13
- static instance() {
14
- if (!formatterWorkerPoolInstance) {
15
- formatterWorkerPoolInstance = new FormatterWorkerPool();
16
+ static instance(opts) {
17
+ if (!formatterWorkerPoolInstance || opts?.forceNew) {
18
+ formatterWorkerPoolInstance = new FormatterWorkerPool(opts?.entrypointURL);
16
19
  }
17
20
  return formatterWorkerPoolInstance;
18
21
  }
@@ -31,7 +34,7 @@ export class FormatterWorkerPool {
31
34
  formatterWorkerPoolInstance = undefined;
32
35
  }
33
36
  createWorker() {
34
- const worker = Platform.HostRuntime.HOST_RUNTIME.createWorker(new URL('../../entrypoints/formatter_worker/formatter_worker-entrypoint.js', import.meta.url).toString());
37
+ const worker = Platform.HostRuntime.HOST_RUNTIME.createWorker(this.entrypointURL);
35
38
  worker.onmessage = this.onWorkerMessage.bind(this, worker);
36
39
  worker.onerror = this.onWorkerError.bind(this, worker);
37
40
  return worker;
@@ -0,0 +1,33 @@
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 Root from '../../core/root/root.js';
6
+ let instance = null;
7
+ export class Prototypes {
8
+ constructor() {
9
+ }
10
+ static instance() {
11
+ if (instance) {
12
+ return instance;
13
+ }
14
+ instance = new Prototypes();
15
+ return instance;
16
+ }
17
+ /**
18
+ * Returns true if the specific setting is turned on AND the GreenDev flag is enabled
19
+ */
20
+ isEnabled(setting) {
21
+ const greendevFlagEnabled = Boolean(Root.Runtime.hostConfig.devToolsGreenDevUi?.enabled);
22
+ return greendevFlagEnabled && this.settings()[setting].get();
23
+ }
24
+ settings() {
25
+ const settings = Common.Settings.Settings.instance();
26
+ const inDevToolsFloaty = settings.createSetting('greendev-in-devtools-floaty-enabled', false, "Local" /* Common.Settings.SettingStorageType.LOCAL */);
27
+ const inlineWidgets = settings.createSetting('greendev-inline-widgets-enabled', false, "Local" /* Common.Settings.SettingStorageType.LOCAL */);
28
+ const aiAnnotations = settings.createSetting('greendev-ai-annotations-enabled', false, "Local" /* Common.Settings.SettingStorageType.LOCAL */);
29
+ const artifactViewer = settings.createSetting('greendev-artifact-viewer-enabled', false, "Local" /* Common.Settings.SettingStorageType.LOCAL */);
30
+ const copyToGemini = settings.createSetting('greendev-copy-to-gemini-enabled', false, "Local" /* Common.Settings.SettingStorageType.LOCAL */);
31
+ return { inDevToolsFloaty, inlineWidgets, aiAnnotations, artifactViewer, copyToGemini };
32
+ }
33
+ }
@@ -0,0 +1,4 @@
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 * from './Prototypes.js';
@@ -9,11 +9,13 @@ export class StackTraceImpl extends Common.ObjectWrapper.ObjectWrapper {
9
9
  super();
10
10
  this.syncFragment = syncFragment;
11
11
  this.asyncFragments = asyncFragments;
12
- syncFragment.stackTraces.add(this);
12
+ const fragment = syncFragment instanceof DebuggableFragmentImpl ? syncFragment.fragment : syncFragment;
13
+ fragment.stackTraces.add(this);
13
14
  this.asyncFragments.forEach(asyncFragment => asyncFragment.fragment.stackTraces.add(this));
14
15
  }
15
16
  }
16
17
  export class FragmentImpl {
18
+ static EMPTY_FRAGMENT = new FragmentImpl();
17
19
  node;
18
20
  stackTraces = new Set();
19
21
  /**
@@ -31,6 +33,9 @@ export class FragmentImpl {
31
33
  this.node = node;
32
34
  }
33
35
  get frames() {
36
+ if (!this.node) {
37
+ return [];
38
+ }
34
39
  const frames = [];
35
40
  for (const node of this.node.getCallStack()) {
36
41
  frames.push(...node.frames);
@@ -65,3 +70,67 @@ export class FrameImpl {
65
70
  this.missingDebugInfo = missingDebugInfo;
66
71
  }
67
72
  }
73
+ /**
74
+ * A DebuggableFragmentImpl wraps an existing FragmentImpl. This is important: We can pause at the
75
+ * same location multiple times and the paused information changes each and everytime while the underlying
76
+ * FragmentImpl will stay the same.
77
+ */
78
+ export class DebuggableFragmentImpl {
79
+ fragment;
80
+ callFrames;
81
+ constructor(fragment, callFrames) {
82
+ this.fragment = fragment;
83
+ this.callFrames = callFrames;
84
+ }
85
+ get frames() {
86
+ if (!this.fragment.node) {
87
+ return [];
88
+ }
89
+ const frames = [];
90
+ let index = 0;
91
+ for (const node of this.fragment.node.getCallStack()) {
92
+ for (const [inlineIdx, frame] of node.frames.entries()) {
93
+ // Create virtual frames for inlined frames.
94
+ const sdkFrame = inlineIdx === 0 ? this.callFrames[index] :
95
+ this.callFrames[index].createVirtualCallFrame(inlineIdx, frame.name ?? '');
96
+ frames.push(new DebuggableFrameImpl(frame, sdkFrame));
97
+ }
98
+ index++;
99
+ }
100
+ return frames;
101
+ }
102
+ }
103
+ /**
104
+ * A DebuggableFrameImpl wraps an existing FrameImpl. This is important: We can pause at the
105
+ * same location multiple times and the paused information changes each and everytime while the underlying
106
+ * FrameImpl will stay the same.
107
+ */
108
+ export class DebuggableFrameImpl {
109
+ #frame;
110
+ #sdkFrame;
111
+ constructor(frame, sdkFrame) {
112
+ this.#frame = frame;
113
+ this.#sdkFrame = sdkFrame;
114
+ }
115
+ get url() {
116
+ return this.#frame.url;
117
+ }
118
+ get uiSourceCode() {
119
+ return this.#frame.uiSourceCode;
120
+ }
121
+ get name() {
122
+ return this.#frame.name;
123
+ }
124
+ get line() {
125
+ return this.#frame.line;
126
+ }
127
+ get column() {
128
+ return this.#frame.column;
129
+ }
130
+ get missingDebugInfo() {
131
+ return this.#frame.missingDebugInfo;
132
+ }
133
+ get sdkFrame() {
134
+ return this.#sdkFrame;
135
+ }
136
+ }
@@ -1,10 +1,12 @@
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
+ var _a;
5
+ import * as Common from '../../core/common/common.js';
4
6
  import * as SDK from '../../core/sdk/sdk.js';
5
7
  // eslint-disable-next-line @devtools/es-modules-import
6
8
  import * as StackTrace from './stack_trace.js';
7
- import { AsyncFragmentImpl, FragmentImpl, FrameImpl, StackTraceImpl } from './StackTraceImpl.js';
9
+ import { AsyncFragmentImpl, DebuggableFragmentImpl, FragmentImpl, FrameImpl, StackTraceImpl } from './StackTraceImpl.js';
8
10
  import { Trie } from './Trie.js';
9
11
  /**
10
12
  * The {@link StackTraceModel} is a thin wrapper around a fragment trie.
@@ -13,54 +15,103 @@ import { Trie } from './Trie.js';
13
15
  */
14
16
  export class StackTraceModel extends SDK.SDKModel.SDKModel {
15
17
  #trie = new Trie();
18
+ #mutex = new Common.Mutex.Mutex();
16
19
  /** @returns the {@link StackTraceModel} for the target, or the model for the primaryPageTarget when passing null/undefined */
17
20
  static #modelForTarget(target) {
18
- const model = (target ?? SDK.TargetManager.TargetManager.instance().primaryPageTarget())?.model(StackTraceModel);
21
+ const model = (target ?? SDK.TargetManager.TargetManager.instance().primaryPageTarget())?.model(_a);
19
22
  if (!model) {
20
23
  throw new Error('Unable to find StackTraceModel');
21
24
  }
22
25
  return model;
23
26
  }
24
27
  async createFromProtocolRuntime(stackTrace, rawFramesToUIFrames) {
25
- const translatePromises = [];
26
- const fragment = this.#createFragment(stackTrace.callFrames);
27
- translatePromises.push(this.#translateFragment(fragment, rawFramesToUIFrames));
28
+ const [syncFragment, asyncFragments] = await Promise.all([
29
+ this.#createFragment(stackTrace.callFrames, rawFramesToUIFrames),
30
+ this.#createAsyncFragments(stackTrace, rawFramesToUIFrames),
31
+ ]);
32
+ return new StackTraceImpl(syncFragment, asyncFragments);
33
+ }
34
+ async createFromDebuggerPaused(pausedDetails, rawFramesToUIFrames) {
35
+ const [syncFragment, asyncFragments] = await Promise.all([
36
+ this.#createDebuggableFragment(pausedDetails, rawFramesToUIFrames),
37
+ this.#createAsyncFragments(pausedDetails, rawFramesToUIFrames),
38
+ ]);
39
+ return new StackTraceImpl(syncFragment, asyncFragments);
40
+ }
41
+ /** Trigger re-translation of all fragments with the provide script in their call stack */
42
+ async scriptInfoChanged(script, translateRawFrames) {
43
+ const release = await this.#mutex.acquire();
44
+ try {
45
+ const translatePromises = [];
46
+ let stackTracesToUpdate = new Set();
47
+ for (const fragment of this.#affectedFragments(script)) {
48
+ // We trigger re-translation only for fragments of leaf-nodes. Any fragment along the ancestor-chain
49
+ // is re-translated as a side-effect.
50
+ // We just need to remember the stack traces of the skipped over fragments, so we can send the
51
+ // UPDATED event also to them.
52
+ if (fragment.node?.children.length === 0) {
53
+ translatePromises.push(this.#translateFragment(fragment, translateRawFrames));
54
+ }
55
+ stackTracesToUpdate = stackTracesToUpdate.union(fragment.stackTraces);
56
+ }
57
+ await Promise.all(translatePromises);
58
+ for (const stackTrace of stackTracesToUpdate) {
59
+ stackTrace.dispatchEventToListeners("UPDATED" /* StackTrace.StackTrace.Events.UPDATED */);
60
+ }
61
+ }
62
+ finally {
63
+ release();
64
+ }
65
+ }
66
+ async #createDebuggableFragment(pausedDetails, rawFramesToUIFrames) {
67
+ const fragment = await this.#createFragment(pausedDetails.callFrames.map(frame => ({
68
+ scriptId: frame.script.scriptId,
69
+ url: frame.script.sourceURL,
70
+ functionName: frame.functionName,
71
+ lineNumber: frame.location().lineNumber,
72
+ columnNumber: frame.location().columnNumber,
73
+ })), rawFramesToUIFrames);
74
+ return new DebuggableFragmentImpl(fragment, pausedDetails.callFrames);
75
+ }
76
+ async #createAsyncFragments(stackTraceOrPausedEvent, rawFramesToUIFrames) {
28
77
  const asyncFragments = [];
29
78
  const debuggerModel = this.target().model(SDK.DebuggerModel.DebuggerModel);
30
79
  if (debuggerModel) {
31
- for await (const { stackTrace: asyncStackTrace, target } of debuggerModel.iterateAsyncParents(stackTrace)) {
32
- const model = StackTraceModel.#modelForTarget(target);
33
- const asyncFragment = model.#createFragment(asyncStackTrace.callFrames);
34
- translatePromises.push(model.#translateFragment(asyncFragment, rawFramesToUIFrames));
35
- asyncFragments.push(new AsyncFragmentImpl(asyncStackTrace.description ?? '', asyncFragment));
80
+ for await (const { stackTrace: asyncStackTrace, target } of debuggerModel.iterateAsyncParents(stackTraceOrPausedEvent)) {
81
+ if (asyncStackTrace.callFrames.length === 0) {
82
+ // Skip empty async fragments, they don't add value.
83
+ continue;
84
+ }
85
+ const model = _a.#modelForTarget(target);
86
+ const asyncFragmentPromise = model.#createFragment(asyncStackTrace.callFrames, rawFramesToUIFrames)
87
+ .then(fragment => new AsyncFragmentImpl(asyncStackTrace.description ?? '', fragment));
88
+ asyncFragments.push(asyncFragmentPromise);
36
89
  }
37
90
  }
38
- await Promise.all(translatePromises);
39
- return new StackTraceImpl(fragment, asyncFragments);
91
+ return await Promise.all(asyncFragments);
40
92
  }
41
- /** Trigger re-translation of all fragments with the provide script in their call stack */
42
- async scriptInfoChanged(script, translateRawFrames) {
43
- const translatePromises = [];
44
- let stackTracesToUpdate = new Set();
45
- for (const fragment of this.#affectedFragments(script)) {
46
- // We trigger re-translation only for fragments of leaf-nodes. Any fragment along the ancestor-chain
47
- // is re-translated as a side-effect.
48
- // We just need to remember the stack traces of the skipped over fragments, so we can send the
49
- // UPDATED event also to them.
50
- if (fragment.node.children.length === 0) {
51
- translatePromises.push(this.#translateFragment(fragment, translateRawFrames));
93
+ async #createFragment(frames, rawFramesToUIFrames) {
94
+ if (frames.length === 0) {
95
+ return FragmentImpl.EMPTY_FRAGMENT;
96
+ }
97
+ const release = await this.#mutex.acquire();
98
+ try {
99
+ const node = this.#trie.insert(frames);
100
+ const requiresTranslation = !Boolean(node.fragment);
101
+ const fragment = FragmentImpl.getOrCreate(node);
102
+ if (requiresTranslation) {
103
+ await this.#translateFragment(fragment, rawFramesToUIFrames);
52
104
  }
53
- stackTracesToUpdate = stackTracesToUpdate.union(fragment.stackTraces);
105
+ return fragment;
54
106
  }
55
- await Promise.all(translatePromises);
56
- for (const stackTrace of stackTracesToUpdate) {
57
- stackTrace.dispatchEventToListeners("UPDATED" /* StackTrace.StackTrace.Events.UPDATED */);
107
+ finally {
108
+ release();
58
109
  }
59
110
  }
60
- #createFragment(frames) {
61
- return FragmentImpl.getOrCreate(this.#trie.insert(frames));
62
- }
63
111
  async #translateFragment(fragment, rawFramesToUIFrames) {
112
+ if (!fragment.node) {
113
+ return;
114
+ }
64
115
  const rawFrames = fragment.node.getCallStack().map(node => node.rawFrame).toArray();
65
116
  const uiFrames = await rawFramesToUIFrames(rawFrames, this.target());
66
117
  console.assert(rawFrames.length === uiFrames.length, 'Broken rawFramesToUIFrames implementation');
@@ -94,4 +145,5 @@ export class StackTraceModel extends SDK.SDKModel.SDKModel {
94
145
  return fragments;
95
146
  }
96
147
  }
148
+ _a = StackTraceModel;
97
149
  SDK.SDKModel.SDKModel.register(StackTraceModel, { capabilities: 0 /* SDK.Target.Capability.NONE */, autostart: false });
@@ -13,9 +13,14 @@ export class EventsSerializer {
13
13
  return `${"l" /* Types.File.EventKeyType.LEGACY_TIMELINE_FRAME */}-${event.index}`;
14
14
  }
15
15
  const rawEvents = Helpers.SyntheticEvents.SyntheticEventsManager.getActiveManager().getRawTraceEvents();
16
+ const isSynthetic = Types.Events.isSyntheticBased(event);
17
+ const index = rawEvents.indexOf(isSynthetic ? event.rawSourceEvent : event);
18
+ if (index === -1) {
19
+ throw new Error(`Unknown trace event: ${event.name}`);
20
+ }
16
21
  const key = Types.Events.isSyntheticBased(event) ?
17
- `${"s" /* Types.File.EventKeyType.SYNTHETIC_EVENT */}-${rawEvents.indexOf(event.rawSourceEvent)}` :
18
- `${"r" /* Types.File.EventKeyType.RAW_EVENT */}-${rawEvents.indexOf(event)}`;
22
+ `${"s" /* Types.File.EventKeyType.SYNTHETIC_EVENT */}-${index}` :
23
+ `${"r" /* Types.File.EventKeyType.RAW_EVENT */}-${index}`;
19
24
  if (key.length < 3) {
20
25
  return null;
21
26
  }
@@ -3,12 +3,12 @@
3
3
  // found in the LICENSE file.
4
4
  import * as Handlers from './handlers/handlers.js';
5
5
  import * as Lantern from './lantern/lantern.js';
6
- function createProcessedNavigation(data, frameId, navigationId) {
6
+ function createProcessedNavigation(data, frameId, navigation) {
7
7
  const scoresByNav = data.PageLoadMetrics.metricScoresByFrameId.get(frameId);
8
8
  if (!scoresByNav) {
9
9
  throw new Lantern.Core.LanternError('missing metric scores for frame');
10
10
  }
11
- const scores = scoresByNav.get(navigationId);
11
+ const scores = scoresByNav.get(navigation);
12
12
  if (!scores) {
13
13
  throw new Lantern.Core.LanternError('missing metric scores for specified navigation');
14
14
  }
@@ -212,7 +212,7 @@ export class TraceProcessor extends EventTarget {
212
212
  }
213
213
  return this.#insights;
214
214
  }
215
- #createLanternContext(data, traceEvents, frameId, navigationId, options) {
215
+ #createLanternContext(data, traceEvents, frameId, navigation, options) {
216
216
  // Check for required handlers.
217
217
  if (!data.NetworkRequests || !data.Workers || !data.PageLoadMetrics) {
218
218
  return;
@@ -221,7 +221,7 @@ export class TraceProcessor extends EventTarget {
221
221
  throw new Lantern.Core.LanternError('No network requests found in trace');
222
222
  }
223
223
  const navStarts = data.Meta.navigationsByFrameId.get(frameId);
224
- const navStartIndex = navStarts?.findIndex(n => n.args.data?.navigationId === navigationId);
224
+ const navStartIndex = navStarts?.findIndex(n => n === navigation);
225
225
  if (!navStarts || navStartIndex === undefined || navStartIndex === -1) {
226
226
  throw new Lantern.Core.LanternError('Could not find navigation start');
227
227
  }
@@ -235,7 +235,7 @@ export class TraceProcessor extends EventTarget {
235
235
  };
236
236
  const requests = LanternComputationData.createNetworkRequests(trace, data, startTime, endTime);
237
237
  const graph = LanternComputationData.createGraph(requests, trace, data);
238
- const processedNavigation = LanternComputationData.createProcessedNavigation(data, frameId, navigationId);
238
+ const processedNavigation = LanternComputationData.createProcessedNavigation(data, frameId, navigation);
239
239
  const networkAnalysis = Lantern.Core.NetworkAnalyzer.analyze(requests);
240
240
  if (!networkAnalysis) {
241
241
  return;
@@ -304,10 +304,10 @@ export class TraceProcessor extends EventTarget {
304
304
  const observedInpScore = Insights.Common.evaluateINPMetricScore(observedInp);
305
305
  const observedClsScore = Insights.Common.evaluateCLSMetricScore(observedCls);
306
306
  const insightToSortingRank = new Map();
307
- for (const [name, model] of Object.entries(insightSet.model)) {
308
- const lcp = model.metricSavings?.LCP ?? 0;
309
- const inp = model.metricSavings?.INP ?? 0;
310
- const cls = model.metricSavings?.CLS ?? 0;
307
+ for (const [name, insight] of Object.entries(insightSet.model)) {
308
+ const lcp = insight.metricSavings?.LCP ?? 0;
309
+ const inp = insight.metricSavings?.INP ?? 0;
310
+ const cls = insight.metricSavings?.CLS ?? 0;
311
311
  const lcpPostSavings = observedLcp !== undefined ? Math.max(0, observedLcp - lcp) : undefined;
312
312
  const inpPostSavings = Math.max(0, observedInp - inp);
313
313
  const clsPostSavings = Math.max(0, observedCls - cls);
@@ -365,28 +365,28 @@ export class TraceProcessor extends EventTarget {
365
365
  urlString = data.Meta.finalDisplayUrlByNavigationId.get('') ?? data.Meta.mainFrameURL;
366
366
  }
367
367
  const insightSetModel = {};
368
+ const insightSetModelErrors = {};
368
369
  for (const [name, insight] of Object.entries(_a.getInsightRunners())) {
369
- let model;
370
370
  try {
371
371
  logger?.start(`insights:${name}`);
372
- model = insight.generateInsight(data, context);
372
+ const model = insight.generateInsight(data, context);
373
373
  model.frameId = context.frameId;
374
374
  const navId = context.navigation?.args.data?.navigationId;
375
375
  if (navId) {
376
- model.navigationId = navId;
376
+ model.navigation = context.navigation;
377
377
  }
378
378
  model.createOverlays = () => {
379
379
  // @ts-expect-error: model is a union of all possible insight model types.
380
380
  return insight.createOverlays(model);
381
381
  };
382
+ Object.assign(insightSetModel, { [name]: model });
382
383
  }
383
384
  catch (err) {
384
- model = err;
385
+ Object.assign(insightSetModelErrors, { [name]: err });
385
386
  }
386
387
  finally {
387
388
  logger?.end(`insights:${name}`);
388
389
  }
389
- Object.assign(insightSetModel, { [name]: model });
390
390
  }
391
391
  // We may choose to exclude the insightSet if it's trivial. Trivial means:
392
392
  // 1. There's no navigation (it's an initial trace period)
@@ -396,12 +396,10 @@ export class TraceProcessor extends EventTarget {
396
396
  // Generally, these cases are the short time ranges before a page reload starts.
397
397
  const isNavigation = id === Types.Events.NO_NAVIGATION;
398
398
  const trivialThreshold = Helpers.Timing.milliToMicro(Types.Timing.Milli(5000));
399
- const everyInsightPasses = Object.values(insightSetModel)
400
- .filter(model => !(model instanceof Error))
401
- .every(model => model.state === 'pass');
402
- const noLcp = !insightSetModel.LCPBreakdown.lcpEvent;
403
- const noInp = !insightSetModel.INPBreakdown.longestInteractionEvent;
404
- const noLayoutShifts = insightSetModel.CLSCulprits.shifts?.size === 0;
399
+ const everyInsightPasses = Object.values(insightSetModel).every(model => model && model.state === 'pass');
400
+ const noLcp = !insightSetModel.LCPBreakdown?.lcpEvent;
401
+ const noInp = !insightSetModel.INPBreakdown?.longestInteractionEvent;
402
+ const noLayoutShifts = insightSetModel.CLSCulprits?.shifts?.size === 0;
405
403
  const shouldExclude = isNavigation && context.bounds.range < trivialThreshold && everyInsightPasses && noLcp &&
406
404
  noInp && noLayoutShifts;
407
405
  if (shouldExclude) {
@@ -423,6 +421,7 @@ export class TraceProcessor extends EventTarget {
423
421
  frameId: context.frameId,
424
422
  bounds: context.bounds,
425
423
  model: insightSetModel,
424
+ modelErrors: insightSetModelErrors,
426
425
  };
427
426
  this.#insights.set(insightSet.id, insightSet);
428
427
  this.sortInsightSet(insightSet, context.options.metadata ?? null);
@@ -474,7 +473,7 @@ export class TraceProcessor extends EventTarget {
474
473
  let lantern;
475
474
  try {
476
475
  options.logger?.start('insights:createLanternContext');
477
- lantern = this.#createLanternContext(data, traceEvents, frameId, navigationId, options);
476
+ lantern = this.#createLanternContext(data, traceEvents, frameId, navigation, options);
478
477
  }
479
478
  catch (e) {
480
479
  // Handle Lantern errors gracefully