chrome-devtools-mcp-for-extension 0.19.2 → 0.20.0

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 (153) hide show
  1. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/Color.js +13 -9
  2. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/ColorConverter.js +9 -7
  3. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/Console.js +1 -8
  4. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/Debouncer.js +2 -2
  5. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/Gzip.js +9 -7
  6. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/MapWithDefault.js +5 -3
  7. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/ParsedURL.js +10 -20
  8. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/ResourceType.js +0 -11
  9. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/ReturnToPanel.js +6 -4
  10. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/Revealer.js +5 -0
  11. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/SegmentedRange.js +1 -2
  12. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/Settings.js +110 -99
  13. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/StringOutputStream.js +1 -4
  14. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/Throttler.js +2 -2
  15. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/common.js +1 -4
  16. package/build/node_modules/chrome-devtools-frontend/front_end/core/host/AidaClient.js +21 -2
  17. package/build/node_modules/chrome-devtools-frontend/front_end/core/host/DispatchHttpRequestClient.js +66 -0
  18. package/build/node_modules/chrome-devtools-frontend/front_end/core/host/GdpClient.js +102 -86
  19. package/build/node_modules/chrome-devtools-frontend/front_end/core/host/InspectorFrontendHost.js +32 -450
  20. package/build/node_modules/chrome-devtools-frontend/front_end/core/host/InspectorFrontendHostAPI.js +32 -29
  21. package/build/node_modules/chrome-devtools-frontend/front_end/core/host/InspectorFrontendHostStub.js +430 -0
  22. package/build/node_modules/chrome-devtools-frontend/front_end/core/host/Platform.js +5 -3
  23. package/build/node_modules/chrome-devtools-frontend/front_end/core/host/ResourceLoader.js +10 -22
  24. package/build/node_modules/chrome-devtools-frontend/front_end/core/host/UserMetrics.js +26 -15
  25. package/build/node_modules/chrome-devtools-frontend/front_end/core/host/host.js +2 -1
  26. package/build/node_modules/chrome-devtools-frontend/front_end/core/platform/ArrayUtilities.js +2 -2
  27. package/build/node_modules/chrome-devtools-frontend/front_end/core/platform/HostRuntime.js +19 -0
  28. package/build/node_modules/chrome-devtools-frontend/front_end/core/platform/StringUtilities.js +34 -32
  29. package/build/node_modules/chrome-devtools-frontend/front_end/core/platform/api/HostRuntime.js +4 -0
  30. package/build/node_modules/chrome-devtools-frontend/front_end/core/platform/api/api.js +5 -0
  31. package/build/node_modules/chrome-devtools-frontend/front_end/core/platform/browser/HostRuntime.js +63 -0
  32. package/build/node_modules/chrome-devtools-frontend/front_end/core/platform/browser/browser.js +5 -0
  33. package/build/node_modules/chrome-devtools-frontend/front_end/core/platform/node/HostRuntime.js +72 -0
  34. package/build/node_modules/chrome-devtools-frontend/front_end/core/platform/node/node.js +5 -0
  35. package/build/node_modules/chrome-devtools-frontend/front_end/core/platform/platform.js +2 -2
  36. package/build/node_modules/chrome-devtools-frontend/front_end/core/protocol_client/CDPConnection.js +17 -0
  37. package/build/node_modules/chrome-devtools-frontend/front_end/core/protocol_client/ConnectionTransport.js +12 -0
  38. package/build/node_modules/chrome-devtools-frontend/front_end/core/protocol_client/DevToolsCDPConnection.js +138 -0
  39. package/build/node_modules/chrome-devtools-frontend/front_end/core/protocol_client/InspectorBackend.js +49 -312
  40. package/build/node_modules/chrome-devtools-frontend/front_end/core/protocol_client/protocol_client.js +4 -9
  41. package/build/node_modules/chrome-devtools-frontend/front_end/core/root/DevToolsContext.js +54 -0
  42. package/build/node_modules/chrome-devtools-frontend/front_end/core/root/root.js +2 -1
  43. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/AnimationModel.js +2 -3
  44. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/CPUThrottlingManager.js +0 -3
  45. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/CSSMatchedStyles.js +51 -17
  46. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/CSSMetadata.js +21 -7
  47. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/CSSModel.js +11 -5
  48. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/CSSProperty.js +4 -7
  49. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/CSSPropertyParser.js +11 -9
  50. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/CSSPropertyParserMatchers.js +34 -24
  51. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/CSSRule.js +52 -10
  52. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/CSSStartingStyle.js +21 -0
  53. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/CSSStyleDeclaration.js +4 -4
  54. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/ChildTargetManager.js +39 -35
  55. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/Connections.js +10 -47
  56. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/ConsoleModel.js +1 -1
  57. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/Cookie.js +1 -1
  58. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/DOMModel.js +29 -4
  59. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/DebuggerModel.js +5 -20
  60. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/EnhancedTracesParser.js +17 -3
  61. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/EventBreakpointsModel.js +4 -2
  62. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/HttpReasonPhraseStrings.js +4 -2
  63. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/IOModel.js +1 -4
  64. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/NetworkManager.js +428 -121
  65. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/NetworkRequest.js +8 -22
  66. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/PageResourceLoader.js +44 -34
  67. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/PreloadingModel.js +64 -19
  68. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/RehydratingConnection.js +139 -12
  69. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/RemoteObject.js +5 -2
  70. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/ResourceTreeModel.js +3 -2
  71. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/ScopeTreeCache.js +36 -0
  72. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/ScreenCaptureModel.js +20 -18
  73. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/Script.js +26 -1
  74. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/ServerSentEventsProtocol.js +3 -0
  75. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/SourceMap.js +54 -14
  76. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/SourceMapCache.js +16 -0
  77. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/SourceMapManager.js +19 -7
  78. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/SourceMapScopesInfo.js +240 -8
  79. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/Target.js +12 -17
  80. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/TargetManager.js +38 -29
  81. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/TraceObject.js +11 -8
  82. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/sdk-meta.js +80 -31
  83. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/sdk.js +4 -2
  84. package/build/node_modules/chrome-devtools-frontend/front_end/generated/ARIAProperties.js +1301 -174
  85. package/build/node_modules/chrome-devtools-frontend/front_end/generated/Deprecation.js +67 -22
  86. package/build/node_modules/chrome-devtools-frontend/front_end/generated/InspectorBackendCommands.js +62 -91
  87. package/build/node_modules/chrome-devtools-frontend/front_end/generated/SupportedCSSProperties.js +188 -100
  88. package/build/node_modules/chrome-devtools-frontend/front_end/models/ai_assistance/data_formatters/NetworkRequestFormatter.js +30 -3
  89. package/build/node_modules/chrome-devtools-frontend/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.js +50 -34
  90. package/build/node_modules/chrome-devtools-frontend/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.js +152 -101
  91. package/build/node_modules/chrome-devtools-frontend/front_end/models/ai_assistance/performance/AICallTree.js +11 -6
  92. package/build/node_modules/chrome-devtools-frontend/front_end/models/ai_assistance/performance/AIContext.js +25 -26
  93. package/build/node_modules/chrome-devtools-frontend/front_end/models/ai_assistance/performance/AIQueries.js +46 -2
  94. package/build/node_modules/chrome-devtools-frontend/front_end/models/bindings/CSSWorkspaceBinding.js +10 -8
  95. package/build/node_modules/chrome-devtools-frontend/front_end/models/bindings/CompilerScriptMapping.js +82 -3
  96. package/build/node_modules/chrome-devtools-frontend/front_end/models/bindings/DebuggerWorkspaceBinding.js +33 -15
  97. package/build/node_modules/chrome-devtools-frontend/front_end/models/bindings/ResourceMapping.js +105 -14
  98. package/build/node_modules/chrome-devtools-frontend/front_end/models/bindings/ResourceScriptMapping.js +38 -0
  99. package/build/node_modules/chrome-devtools-frontend/front_end/models/bindings/SASSSourceMapping.js +5 -4
  100. package/build/node_modules/chrome-devtools-frontend/front_end/models/cpu_profile/CPUProfileDataModel.js +9 -7
  101. package/build/node_modules/chrome-devtools-frontend/front_end/models/cpu_profile/ProfileTreeModel.js +6 -7
  102. package/build/node_modules/chrome-devtools-frontend/front_end/models/crux-manager/CrUXManager.js +6 -4
  103. package/build/node_modules/chrome-devtools-frontend/front_end/models/formatter/FormatterWorkerPool.js +16 -2
  104. package/build/node_modules/chrome-devtools-frontend/front_end/models/network_time_calculator/RequestTimeRanges.js +6 -4
  105. package/build/node_modules/chrome-devtools-frontend/front_end/models/source_map_scopes/FunctionCodeResolver.js +192 -0
  106. package/build/node_modules/chrome-devtools-frontend/front_end/models/source_map_scopes/NamesResolver.js +12 -16
  107. package/build/node_modules/chrome-devtools-frontend/front_end/models/source_map_scopes/source_map_scopes.js +2 -2
  108. package/build/node_modules/chrome-devtools-frontend/front_end/models/stack_trace/StackTraceModel.js +1 -1
  109. package/build/node_modules/chrome-devtools-frontend/front_end/models/stack_trace/Trie.js +8 -0
  110. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/LanternComputationData.js +1 -0
  111. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/ModelImpl.js +6 -3
  112. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/Processor.js +4 -4
  113. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/Styles.js +1 -1
  114. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/extras/TraceTree.js +11 -4
  115. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/handlers/FramesHandler.js +7 -5
  116. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/handlers/LayoutShiftsHandler.js +8 -4
  117. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/handlers/MetaHandler.js +4 -1
  118. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/handlers/NetworkRequestsHandler.js +29 -3
  119. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/handlers/SamplesHandler.js +59 -6
  120. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/handlers/ScriptsHandler.js +26 -1
  121. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/handlers/UserTimingsHandler.js +1 -1
  122. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/handlers/helpers.js +1 -1
  123. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/helpers/Timing.js +4 -2
  124. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/helpers/Trace.js +18 -4
  125. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/CLSCulprits.js +2 -1
  126. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/Cache.js +2 -1
  127. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/DOMSize.js +2 -1
  128. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/DocumentLatency.js +12 -11
  129. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/DuplicatedJavaScript.js +2 -1
  130. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/FontDisplay.js +2 -1
  131. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/ForcedReflow.js +3 -2
  132. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/INPBreakdown.js +14 -2
  133. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/ImageDelivery.js +2 -1
  134. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/LCPBreakdown.js +13 -2
  135. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/LCPDiscovery.js +2 -1
  136. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/LegacyJavaScript.js +4 -2
  137. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/ModernHTTP.js +2 -1
  138. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/NetworkDependencyTree.js +4 -3
  139. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/RenderBlocking.js +2 -1
  140. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/SlowCSSSelector.js +2 -1
  141. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/ThirdParties.js +2 -1
  142. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/Viewport.js +2 -1
  143. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/types/TraceEvents.js +9 -3
  144. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace_source_maps_resolver/SourceMapsResolver.js +25 -2
  145. package/build/node_modules/chrome-devtools-frontend/front_end/models/workspace/IgnoreListManager.js +41 -48
  146. package/build/node_modules/chrome-devtools-frontend/front_end/models/workspace/UISourceCode.js +38 -0
  147. package/build/node_modules/chrome-devtools-frontend/front_end/models/workspace/WorkspaceImpl.js +5 -8
  148. package/build/node_modules/chrome-devtools-frontend/front_end/models/workspace/workspace-meta.js +34 -0
  149. package/build/src/McpContext.js +8 -1
  150. package/build/src/tools/bookmarks.js +0 -1
  151. package/build/src/tools/gemini-web.js +17 -25
  152. package/build/src/tools/snapshot.js +9 -2
  153. package/package.json +3 -3
@@ -29,6 +29,9 @@ export class AIQueries {
29
29
  }
30
30
  const threads = Trace.Handlers.Threads.threadsInTrace(parsedTrace.data);
31
31
  const thread = threads.find(thread => {
32
+ if (!thread.processIsOnMainFrame) {
33
+ return false;
34
+ }
32
35
  if (mainThreadPID && mainThreadTID) {
33
36
  return thread.pid === mainThreadPID && thread.tid === mainThreadTID;
34
37
  }
@@ -37,9 +40,9 @@ export class AIQueries {
37
40
  return thread ?? null;
38
41
  }
39
42
  /**
40
- * Returns bottom up activity for the given range.
43
+ * Returns bottom up activity for the given range (within a single navigation / thread).
41
44
  */
42
- static mainThreadActivityBottomUp(navigationId, bounds, parsedTrace) {
45
+ static mainThreadActivityBottomUpSingleNavigation(navigationId, bounds, parsedTrace) {
43
46
  const thread = this.findMainThread(navigationId, parsedTrace);
44
47
  if (!thread) {
45
48
  return null;
@@ -61,6 +64,47 @@ export class AIQueries {
61
64
  endTime,
62
65
  });
63
66
  }
67
+ /**
68
+ * Returns bottom up activity for the given range (no matter the navigation / thread).
69
+ */
70
+ static mainThreadActivityBottomUp(bounds, parsedTrace) {
71
+ const threads = [];
72
+ if (parsedTrace.insights) {
73
+ for (const insightSet of parsedTrace.insights?.values()) {
74
+ const thread = this.findMainThread(insightSet.navigation?.args.data?.navigationId, parsedTrace);
75
+ if (thread) {
76
+ threads.push(thread);
77
+ }
78
+ }
79
+ }
80
+ else {
81
+ const navigationId = parsedTrace.data.Meta.mainFrameNavigations[0].args.data?.navigationId;
82
+ const thread = this.findMainThread(navigationId, parsedTrace);
83
+ if (thread) {
84
+ threads.push(thread);
85
+ }
86
+ }
87
+ if (threads.length === 0) {
88
+ return null;
89
+ }
90
+ const threadEvents = [...new Set(threads)].map(thread => AICallTree.findEventsForThread({ thread, parsedTrace, bounds }) ?? []);
91
+ const events = threadEvents.flat();
92
+ if (events.length === 0) {
93
+ return null;
94
+ }
95
+ // Use the same filtering as front_end/panels/timeline/TimelineTreeView.ts.
96
+ const visibleEvents = Trace.Helpers.Trace.VISIBLE_TRACE_EVENT_TYPES.values().toArray();
97
+ const filter = new Trace.Extras.TraceFilter.VisibleEventsFilter(visibleEvents.concat(["SyntheticNetworkRequest" /* Trace.Types.Events.Name.SYNTHETIC_NETWORK_REQUEST */]));
98
+ // The bottom up root node handles all the "in Tracebounds" checks we need for the insight.
99
+ const startTime = Trace.Helpers.Timing.microToMilli(bounds.min);
100
+ const endTime = Trace.Helpers.Timing.microToMilli(bounds.max);
101
+ return new Trace.Extras.TraceTree.BottomUpRootNode(events, {
102
+ textFilter: new Trace.Extras.TraceFilter.ExclusiveNameFilter([]),
103
+ filters: [filter],
104
+ startTime,
105
+ endTime,
106
+ });
107
+ }
64
108
  /**
65
109
  * Returns an AI Call Tree representing the activity on the main thread for
66
110
  * the relevant time range of the given insight.
@@ -3,33 +3,34 @@
3
3
  // found in the LICENSE file.
4
4
  import * as Common from '../../core/common/common.js';
5
5
  import * as Platform from '../../core/platform/platform.js';
6
+ import * as Root from '../../core/root/root.js';
6
7
  import * as SDK from '../../core/sdk/sdk.js';
7
8
  import { LiveLocationWithPool, } from './LiveLocation.js';
8
9
  import { SASSSourceMapping } from './SASSSourceMapping.js';
9
10
  import { StylesSourceMapping } from './StylesSourceMapping.js';
10
- let cssWorkspaceBindingInstance;
11
11
  export class CSSWorkspaceBinding {
12
12
  #resourceMapping;
13
13
  #modelToInfo;
14
14
  #liveLocationPromises;
15
15
  constructor(resourceMapping, targetManager) {
16
16
  this.#resourceMapping = resourceMapping;
17
+ this.#resourceMapping.cssWorkspaceBinding = this;
17
18
  this.#modelToInfo = new Map();
18
19
  targetManager.observeModels(SDK.CSSModel.CSSModel, this);
19
20
  this.#liveLocationPromises = new Set();
20
21
  }
21
22
  static instance(opts = { forceNew: null, resourceMapping: null, targetManager: null }) {
22
23
  const { forceNew, resourceMapping, targetManager } = opts;
23
- if (!cssWorkspaceBindingInstance || forceNew) {
24
+ if (forceNew) {
24
25
  if (!resourceMapping || !targetManager) {
25
26
  throw new Error(`Unable to create CSSWorkspaceBinding: resourceMapping and targetManager must be provided: ${new Error().stack}`);
26
27
  }
27
- cssWorkspaceBindingInstance = new CSSWorkspaceBinding(resourceMapping, targetManager);
28
+ Root.DevToolsContext.globalInstance().set(CSSWorkspaceBinding, new CSSWorkspaceBinding(resourceMapping, targetManager));
28
29
  }
29
- return cssWorkspaceBindingInstance;
30
+ return Root.DevToolsContext.globalInstance().get(CSSWorkspaceBinding);
30
31
  }
31
32
  static removeInstance() {
32
- cssWorkspaceBindingInstance = undefined;
33
+ Root.DevToolsContext.globalInstance().delete(CSSWorkspaceBinding);
33
34
  }
34
35
  get modelToInfo() {
35
36
  return this.#modelToInfo;
@@ -38,7 +39,7 @@ export class CSSWorkspaceBinding {
38
39
  return this.#modelToInfo.get(cssModel);
39
40
  }
40
41
  modelAdded(cssModel) {
41
- this.#modelToInfo.set(cssModel, new ModelInfo(cssModel, this.#resourceMapping));
42
+ this.#modelToInfo.set(cssModel, new ModelInfo(cssModel, this.#resourceMapping, this));
42
43
  }
43
44
  modelRemoved(cssModel) {
44
45
  this.getCSSModelInfo(cssModel).dispose();
@@ -109,7 +110,7 @@ export class ModelInfo {
109
110
  #sassSourceMapping;
110
111
  #locations;
111
112
  #unboundLocations;
112
- constructor(cssModel, resourceMapping) {
113
+ constructor(cssModel, resourceMapping, cssWorkspaceBinding) {
113
114
  this.#eventListeners = [
114
115
  cssModel.addEventListener(SDK.CSSModel.Events.StyleSheetAdded, event => {
115
116
  void this.styleSheetAdded(event);
@@ -121,7 +122,8 @@ export class ModelInfo {
121
122
  this.#resourceMapping = resourceMapping;
122
123
  this.#stylesSourceMapping = new StylesSourceMapping(cssModel, resourceMapping.workspace);
123
124
  const sourceMapManager = cssModel.sourceMapManager();
124
- this.#sassSourceMapping = new SASSSourceMapping(cssModel.target(), sourceMapManager, resourceMapping.workspace);
125
+ this.#sassSourceMapping =
126
+ new SASSSourceMapping(cssModel.target(), sourceMapManager, resourceMapping.workspace, cssWorkspaceBinding);
125
127
  this.#locations = new Platform.MapUtilities.Multimap();
126
128
  this.#unboundLocations = new Platform.MapUtilities.Multimap();
127
129
  }
@@ -4,6 +4,8 @@
4
4
  import * as Common from '../../core/common/common.js';
5
5
  import * as Platform from '../../core/platform/platform.js';
6
6
  import * as SDK from '../../core/sdk/sdk.js';
7
+ // eslint-disable-next-line @devtools/es-modules-import
8
+ import * as StackTraceImpl from '../stack_trace/stack_trace_impl.js';
7
9
  import * as TextUtils from '../text_utils/text_utils.js';
8
10
  import * as Workspace from '../workspace/workspace.js';
9
11
  import { ContentProviderBasedProject } from './ContentProviderBasedProject.js';
@@ -37,9 +39,13 @@ export class CompilerScriptMapping {
37
39
  #projects = new Map();
38
40
  #sourceMapToProject = new Map();
39
41
  #uiSourceCodeToSourceMaps = new Platform.MapUtilities.Multimap();
42
+ #debuggerModel;
43
+ #ignoreListManager;
40
44
  constructor(debuggerModel, workspace, debuggerWorkspaceBinding) {
41
45
  this.#sourceMapManager = debuggerModel.sourceMapManager();
42
46
  this.#debuggerWorkspaceBinding = debuggerWorkspaceBinding;
47
+ this.#debuggerModel = debuggerModel;
48
+ this.#ignoreListManager = debuggerWorkspaceBinding.ignoreListManager;
43
49
  this.#stubProject = new ContentProviderBasedProject(workspace, 'jsSourceMaps:stub:' + debuggerModel.target().id(), Workspace.Workspace.projectTypes.Service, '', true /* isServiceProject */);
44
50
  this.#eventListeners = [
45
51
  this.#sourceMapManager.addEventListener(SDK.SourceMapManager.Events.SourceMapWillAttach, this.sourceMapWillAttach, this),
@@ -214,8 +220,81 @@ export class CompilerScriptMapping {
214
220
  }
215
221
  return ranges;
216
222
  }
217
- translateRawFramesStep(_rawFrames, _translatedFrames) {
218
- // TODO(crbug.com/433162438): Implement source map stack trace translation.
223
+ async functionBoundsAtRawLocation(rawLocation) {
224
+ const script = rawLocation.script();
225
+ if (!script) {
226
+ return null;
227
+ }
228
+ const sourceMap = this.#sourceMapManager.sourceMapForClient(script);
229
+ if (!sourceMap) {
230
+ return null;
231
+ }
232
+ const { lineNumber, columnNumber } = script.rawLocationToRelativeLocation(rawLocation);
233
+ const { url, scope } = sourceMap.findOriginalFunctionScope({ line: lineNumber, column: columnNumber }) ?? {};
234
+ if (!scope || !url) {
235
+ return null;
236
+ }
237
+ const project = this.#sourceMapToProject.get(sourceMap);
238
+ if (!project) {
239
+ return null;
240
+ }
241
+ const uiSourceCode = project.uiSourceCodeForURL(url);
242
+ if (!uiSourceCode) {
243
+ return null;
244
+ }
245
+ // If there's no original source content, callers can't get the source code for
246
+ // the given scope. Presently that's the only reason to get a function's bounds,
247
+ // so in that case return null and allow ResourceScriptMapping to fulfill this
248
+ // request.
249
+ const contentData = await uiSourceCode.requestContentData();
250
+ if ('error' in contentData) {
251
+ return null;
252
+ }
253
+ const name = scope.name ?? '';
254
+ const range = new TextUtils.TextRange.TextRange(scope.start.line, scope.start.column, scope.end.line, scope.end.column);
255
+ return new Workspace.UISourceCode.UIFunctionBounds(uiSourceCode, range, name);
256
+ }
257
+ translateRawFramesStep(rawFrames, translatedFrames) {
258
+ const frame = rawFrames[0];
259
+ if (StackTraceImpl.Trie.isBuiltinFrame(frame)) {
260
+ return false;
261
+ }
262
+ const sourceMapWithScopeInfoForFrame = (rawFrame) => {
263
+ const script = this.#debuggerModel.scriptForId(rawFrame.scriptId ?? '');
264
+ if (!script || this.#stubUISourceCodes.has(script)) {
265
+ // Use fallback while source map is being loaded.
266
+ return null;
267
+ }
268
+ const sourceMap = script.sourceMap();
269
+ return sourceMap?.hasScopeInfo() ? { sourceMap, script } : null;
270
+ };
271
+ const sourceMapAndScript = sourceMapWithScopeInfoForFrame(frame);
272
+ if (!sourceMapAndScript) {
273
+ return false;
274
+ }
275
+ const { sourceMap, script } = sourceMapAndScript;
276
+ const { lineNumber, columnNumber } = script.relativeLocationToRawLocation(frame);
277
+ if (!sourceMap.isOutlinedFrame(lineNumber, columnNumber)) {
278
+ const frames = sourceMap.translateCallSite(lineNumber, columnNumber);
279
+ if (!frames.length) {
280
+ return false;
281
+ }
282
+ rawFrames.shift();
283
+ const result = [];
284
+ translatedFrames.push(result);
285
+ const project = this.#sourceMapToProject.get(sourceMap);
286
+ for (const frame of frames) {
287
+ // Switch out url for UISourceCode where we have it.
288
+ const uiSourceCode = frame.url ? project?.uiSourceCodeForURL(frame.url) : undefined;
289
+ result.push({
290
+ ...frame,
291
+ url: uiSourceCode ? undefined : frame.url,
292
+ uiSourceCode: uiSourceCode ?? undefined,
293
+ });
294
+ }
295
+ return true;
296
+ }
297
+ // TODO(crbug.com/433162438): Consolidate outlined frames.
219
298
  return false;
220
299
  }
221
300
  /**
@@ -255,7 +334,7 @@ export class CompilerScriptMapping {
255
334
  // Create stub UISourceCode for the time source mapping is being loaded.
256
335
  this.addStubUISourceCode(script);
257
336
  void this.#debuggerWorkspaceBinding.updateLocations(script);
258
- if (Workspace.IgnoreListManager.IgnoreListManager.instance().isUserIgnoreListedURL(script.sourceURL, { isContentScript: script.isContentScript() })) {
337
+ if (this.#ignoreListManager.isUserIgnoreListedURL(script.sourceURL, { isContentScript: script.isContentScript() })) {
259
338
  this.#sourceMapManager.cancelAttachSourceMap(script);
260
339
  }
261
340
  }
@@ -3,8 +3,9 @@
3
3
  // found in the LICENSE file.
4
4
  import * as Common from '../../core/common/common.js';
5
5
  import * as Platform from '../../core/platform/platform.js';
6
+ import * as Root from '../../core/root/root.js';
6
7
  import * as SDK from '../../core/sdk/sdk.js';
7
- // eslint-disable-next-line rulesdir/es-modules-import
8
+ // eslint-disable-next-line @devtools/es-modules-import
8
9
  import * as StackTraceImpl from '../stack_trace/stack_trace_impl.js';
9
10
  import * as Workspace from '../workspace/workspace.js';
10
11
  import { CompilerScriptMapping } from './CompilerScriptMapping.js';
@@ -13,19 +14,23 @@ import { DefaultScriptMapping } from './DefaultScriptMapping.js';
13
14
  import { LiveLocationWithPool } from './LiveLocation.js';
14
15
  import { NetworkProject } from './NetworkProject.js';
15
16
  import { ResourceScriptMapping } from './ResourceScriptMapping.js';
16
- let debuggerWorkspaceBindingInstance;
17
17
  export class DebuggerWorkspaceBinding {
18
18
  resourceMapping;
19
19
  #debuggerModelToData;
20
20
  #liveLocationPromises;
21
21
  pluginManager;
22
- constructor(resourceMapping, targetManager, ignoreListManager) {
22
+ ignoreListManager;
23
+ workspace;
24
+ constructor(resourceMapping, targetManager, ignoreListManager, workspace) {
23
25
  this.resourceMapping = resourceMapping;
26
+ this.resourceMapping.debuggerWorkspaceBinding = this;
27
+ this.ignoreListManager = ignoreListManager;
28
+ this.workspace = workspace;
24
29
  this.#debuggerModelToData = new Map();
25
30
  targetManager.addModelListener(SDK.DebuggerModel.DebuggerModel, SDK.DebuggerModel.Events.GlobalObjectCleared, this.globalObjectCleared, this);
26
31
  targetManager.addModelListener(SDK.DebuggerModel.DebuggerModel, SDK.DebuggerModel.Events.DebuggerResumed, this.debuggerResumed, this);
27
32
  targetManager.observeModels(SDK.DebuggerModel.DebuggerModel, this);
28
- ignoreListManager.addEventListener("IGNORED_SCRIPT_RANGES_UPDATED" /* Workspace.IgnoreListManager.Events.IGNORED_SCRIPT_RANGES_UPDATED */, event => this.updateLocations(event.data));
33
+ this.ignoreListManager.addEventListener("IGNORED_SCRIPT_RANGES_UPDATED" /* Workspace.IgnoreListManager.Events.IGNORED_SCRIPT_RANGES_UPDATED */, event => this.updateLocations(event.data));
29
34
  this.#liveLocationPromises = new Set();
30
35
  this.pluginManager = new DebuggerLanguagePluginManager(targetManager, resourceMapping.workspace, this);
31
36
  }
@@ -34,19 +39,18 @@ export class DebuggerWorkspaceBinding {
34
39
  modelData.compilerMapping.setFunctionRanges(uiSourceCode, ranges);
35
40
  }
36
41
  }
37
- static instance(opts = { forceNew: null, resourceMapping: null, targetManager: null, ignoreListManager: null }) {
38
- const { forceNew, resourceMapping, targetManager, ignoreListManager } = opts;
39
- if (!debuggerWorkspaceBindingInstance || forceNew) {
40
- if (!resourceMapping || !targetManager || !ignoreListManager) {
42
+ static instance(opts = { forceNew: null, resourceMapping: null, targetManager: null, ignoreListManager: null, workspace: null }) {
43
+ const { forceNew, resourceMapping, targetManager, ignoreListManager, workspace } = opts;
44
+ if (forceNew) {
45
+ if (!resourceMapping || !targetManager || !ignoreListManager || !workspace) {
41
46
  throw new Error(`Unable to create DebuggerWorkspaceBinding: resourceMapping, targetManager and IgnoreLIstManager must be provided: ${new Error().stack}`);
42
47
  }
43
- debuggerWorkspaceBindingInstance =
44
- new DebuggerWorkspaceBinding(resourceMapping, targetManager, ignoreListManager);
48
+ Root.DevToolsContext.globalInstance().set(DebuggerWorkspaceBinding, new DebuggerWorkspaceBinding(resourceMapping, targetManager, ignoreListManager, workspace));
45
49
  }
46
- return debuggerWorkspaceBindingInstance;
50
+ return Root.DevToolsContext.globalInstance().get(DebuggerWorkspaceBinding);
47
51
  }
48
52
  static removeInstance() {
49
- debuggerWorkspaceBindingInstance = undefined;
53
+ Root.DevToolsContext.globalInstance().delete(DebuggerWorkspaceBinding);
50
54
  }
51
55
  async computeAutoStepRanges(mode, callFrame) {
52
56
  function contained(location, range) {
@@ -199,11 +203,10 @@ export class DebuggerWorkspaceBinding {
199
203
  }
200
204
  waitForUISourceCodeAdded(url, target) {
201
205
  return new Promise(resolve => {
202
- const workspace = Workspace.Workspace.WorkspaceImpl.instance();
203
- const descriptor = workspace.addEventListener(Workspace.Workspace.Events.UISourceCodeAdded, event => {
206
+ const descriptor = this.workspace.addEventListener(Workspace.Workspace.Events.UISourceCodeAdded, event => {
204
207
  const uiSourceCode = event.data;
205
208
  if (uiSourceCode.url() === url && NetworkProject.targetForUISourceCode(uiSourceCode) === target) {
206
- workspace.removeEventListener(Workspace.Workspace.Events.UISourceCodeAdded, descriptor.listener);
209
+ this.workspace.removeEventListener(Workspace.Workspace.Events.UISourceCodeAdded, descriptor.listener);
207
210
  resolve(uiSourceCode);
208
211
  }
209
212
  });
@@ -255,6 +258,11 @@ export class DebuggerWorkspaceBinding {
255
258
  }
256
259
  return [];
257
260
  }
261
+ async functionBoundsAtRawLocation(rawLocation) {
262
+ // TODO(crbug.com/463452667): first try pluginManager.
263
+ const modelData = this.#debuggerModelToData.get(rawLocation.debuggerModel);
264
+ return modelData ? await modelData.functionBoundsAtRawLocation(rawLocation) : null;
265
+ }
258
266
  async normalizeUILocation(uiLocation) {
259
267
  const rawLocations = await this.uiLocationToRawLocations(uiLocation.uiSourceCode, uiLocation.lineNumber, uiLocation.columnNumber);
260
268
  for (const location of rawLocations) {
@@ -455,6 +463,16 @@ class ModelData {
455
463
  ranges ??= this.#defaultMapping.uiLocationRangeToRawLocationRanges(uiSourceCode, textRange);
456
464
  return ranges;
457
465
  }
466
+ async functionBoundsAtRawLocation(rawLocation) {
467
+ let scope = null;
468
+ // Check source maps.
469
+ scope = scope || await this.compilerMapping.functionBoundsAtRawLocation(rawLocation);
470
+ // Check debugger scripts.
471
+ scope = scope || await this.#resourceScriptMapping.functionBoundsAtRawLocation(rawLocation);
472
+ // Check inline scripts inside HTML resources.
473
+ scope = scope || await this.#resourceMapping.functionBoundsAtRawLocation(rawLocation);
474
+ return scope;
475
+ }
458
476
  translateRawFramesStep(rawFrames, translatedFrames) {
459
477
  if (!this.compilerMapping.translateRawFramesStep(rawFrames, translatedFrames)) {
460
478
  this.#defaultTranslateRawFramesStep(rawFrames, translatedFrames);
@@ -3,11 +3,10 @@
3
3
  // found in the LICENSE file.
4
4
  import * as Common from '../../core/common/common.js';
5
5
  import * as SDK from '../../core/sdk/sdk.js';
6
+ import * as Formatter from '../formatter/formatter.js';
6
7
  import * as TextUtils from '../text_utils/text_utils.js';
7
8
  import * as Workspace from '../workspace/workspace.js';
8
9
  import { ContentProviderBasedProject } from './ContentProviderBasedProject.js';
9
- import { CSSWorkspaceBinding } from './CSSWorkspaceBinding.js';
10
- import { DebuggerWorkspaceBinding } from './DebuggerWorkspaceBinding.js';
11
10
  import { NetworkProject } from './NetworkProject.js';
12
11
  import { resourceMetadata } from './ResourceUtils.js';
13
12
  const styleSheetRangeMap = new WeakMap();
@@ -22,12 +21,40 @@ function computeStyleSheetRange(header) {
22
21
  export class ResourceMapping {
23
22
  workspace;
24
23
  #modelToInfo = new Map();
24
+ #debuggerWorkspaceBinding = null;
25
+ #cssWorkspaceBinding = null;
25
26
  constructor(targetManager, workspace) {
26
27
  this.workspace = workspace;
27
28
  targetManager.observeModels(SDK.ResourceTreeModel.ResourceTreeModel, this);
28
29
  }
30
+ get debuggerWorkspaceBinding() {
31
+ // TODO(crbug.com/458180550): Throw when this.#debuggerWorkspaceBinding is null and never return null.
32
+ // The only reason we don't throw and return an instance unconditionally
33
+ // is that unit tests often don't set-up both the *WorkspaceBindings.
34
+ return this.#debuggerWorkspaceBinding;
35
+ }
36
+ /* {@link DebuggerWorkspaceBinding} and ResourceMapping form a cycle so we can't wire it up at ctor time. */
37
+ set debuggerWorkspaceBinding(debuggerWorkspaceBinding) {
38
+ if (this.#debuggerWorkspaceBinding) {
39
+ throw new Error('DebuggerWorkspaceBinding already set');
40
+ }
41
+ this.#debuggerWorkspaceBinding = debuggerWorkspaceBinding;
42
+ }
43
+ get cssWorkspaceBinding() {
44
+ // TODO(crbug.com/458180550): Throw when this.#cssWorkspaceBinding is null and never return null.
45
+ // The only reason we don't throw and return an instance unconditionally
46
+ // is that unit tests often don't set-up both the *WorkspaceBindings.
47
+ return this.#cssWorkspaceBinding;
48
+ }
49
+ /* {@link CSSWorkspaceBinding} and ResourceMapping form a cycle so we can't wire it up at ctor time. */
50
+ set cssWorkspaceBinding(cssWorkspaceBinding) {
51
+ if (this.#cssWorkspaceBinding) {
52
+ throw new Error('CSSWorkspaceBinding already set');
53
+ }
54
+ this.#cssWorkspaceBinding = cssWorkspaceBinding;
55
+ }
29
56
  modelAdded(resourceTreeModel) {
30
- const info = new ModelInfo(this.workspace, resourceTreeModel);
57
+ const info = new ModelInfo(this, resourceTreeModel);
31
58
  this.#modelToInfo.set(resourceTreeModel, info);
32
59
  }
33
60
  modelRemoved(resourceTreeModel) {
@@ -212,6 +239,64 @@ export class ResourceMapping {
212
239
  }
213
240
  return cssModel.createRawLocationsByURL(uiLocation.uiSourceCode.url(), uiLocation.lineNumber, uiLocation.columnNumber);
214
241
  }
242
+ async functionBoundsAtRawLocation(rawLocation) {
243
+ const script = rawLocation.script();
244
+ if (!script) {
245
+ return null;
246
+ }
247
+ const info = this.infoForTarget(script.debuggerModel.target());
248
+ if (!info) {
249
+ return null;
250
+ }
251
+ const embedderName = script.embedderName();
252
+ if (!embedderName) {
253
+ return null;
254
+ }
255
+ const uiSourceCode = info.getProject().uiSourceCodeForURL(embedderName);
256
+ if (!uiSourceCode) {
257
+ return null;
258
+ }
259
+ let { lineNumber, columnNumber } = rawLocation;
260
+ lineNumber -= script.lineOffset;
261
+ if (lineNumber === 0) {
262
+ columnNumber -= script.columnOffset;
263
+ }
264
+ const scopeTreeAndText = script ? await SDK.ScopeTreeCache.scopeTreeForScript(script) : null;
265
+ if (!scopeTreeAndText) {
266
+ return null;
267
+ }
268
+ // Find the inner-most scope that maps to the given position.
269
+ const offset = scopeTreeAndText.text.offsetFromPosition(lineNumber, columnNumber);
270
+ const results = [];
271
+ (function walk(nodes) {
272
+ for (const node of nodes) {
273
+ if (!(offset >= node.start && offset < node.end)) {
274
+ continue;
275
+ }
276
+ results.push(node);
277
+ walk(node.children);
278
+ }
279
+ })([scopeTreeAndText.scopeTree]);
280
+ const result = results.findLast(node => node.kind === 2 /* Formatter.FormatterWorkerPool.ScopeKind.FUNCTION */ ||
281
+ node.kind === 4 /* Formatter.FormatterWorkerPool.ScopeKind.ARROW_FUNCTION */);
282
+ if (!result) {
283
+ return null;
284
+ }
285
+ // Map back to positions.
286
+ const startPosition = scopeTreeAndText.text.positionFromOffset(result.start);
287
+ const endPosition = scopeTreeAndText.text.positionFromOffset(result.end);
288
+ startPosition.lineNumber += script.lineOffset;
289
+ if (startPosition.lineNumber === script.lineOffset) {
290
+ startPosition.columnNumber += script.columnOffset;
291
+ }
292
+ endPosition.lineNumber += script.lineOffset;
293
+ if (endPosition.lineNumber === script.lineOffset) {
294
+ endPosition.columnNumber += script.columnOffset;
295
+ }
296
+ const name = ''; // TODO(crbug.com/452333154): update ScopeVariableAnalysis to include function name.
297
+ const range = new TextUtils.TextRange.TextRange(startPosition.lineNumber, startPosition.columnNumber, endPosition.lineNumber, endPosition.columnNumber);
298
+ return new Workspace.UISourceCode.UIFunctionBounds(uiSourceCode, range, name);
299
+ }
215
300
  resetForTest(target) {
216
301
  const resourceTreeModel = target.model(SDK.ResourceTreeModel.ResourceTreeModel);
217
302
  const info = resourceTreeModel ? this.#modelToInfo.get(resourceTreeModel) : null;
@@ -225,9 +310,11 @@ class ModelInfo {
225
310
  #bindings = new Map();
226
311
  #cssModel;
227
312
  #eventListeners;
228
- constructor(workspace, resourceTreeModel) {
313
+ resourceMapping;
314
+ constructor(resourceMapping, resourceTreeModel) {
229
315
  const target = resourceTreeModel.target();
230
- this.project = new ContentProviderBasedProject(workspace, 'resources:' + target.id(), Workspace.Workspace.projectTypes.Network, '', false /* isServiceProject */);
316
+ this.resourceMapping = resourceMapping;
317
+ this.project = new ContentProviderBasedProject(resourceMapping.workspace, 'resources:' + target.id(), Workspace.Workspace.projectTypes.Network, '', false /* isServiceProject */);
231
318
  NetworkProject.setTargetForProject(this.project, target);
232
319
  const cssModel = target.model(SDK.CSSModel.CSSModel);
233
320
  console.assert(Boolean(cssModel));
@@ -293,7 +380,7 @@ class ModelInfo {
293
380
  }
294
381
  let binding = this.#bindings.get(resource.url);
295
382
  if (!binding) {
296
- binding = new Binding(this.project, resource);
383
+ binding = new Binding(this, resource);
297
384
  this.#bindings.set(resource.url, binding);
298
385
  }
299
386
  else {
@@ -347,9 +434,13 @@ class Binding {
347
434
  #project;
348
435
  #uiSourceCode;
349
436
  #edits = [];
350
- constructor(project, resource) {
437
+ #debuggerWorkspaceBinding;
438
+ #cssWorkspaceBinding;
439
+ constructor(modelInfo, resource) {
351
440
  this.resources = new Set([resource]);
352
- this.#project = project;
441
+ this.#project = modelInfo.project;
442
+ this.#debuggerWorkspaceBinding = modelInfo.resourceMapping.debuggerWorkspaceBinding;
443
+ this.#cssWorkspaceBinding = modelInfo.resourceMapping.cssWorkspaceBinding;
353
444
  this.#uiSourceCode = this.#project.createUISourceCode(resource.url, resource.contentType());
354
445
  boundUISourceCodes.add(this.#uiSourceCode);
355
446
  if (resource.frameId) {
@@ -357,8 +448,8 @@ class Binding {
357
448
  }
358
449
  this.#project.addUISourceCodeWithProvider(this.#uiSourceCode, this, resourceMetadata(resource), resource.mimeType);
359
450
  void Promise.all([
360
- ...this.inlineScripts().map(script => DebuggerWorkspaceBinding.instance().updateLocations(script)),
361
- ...this.inlineStyles().map(style => CSSWorkspaceBinding.instance().updateLocations(style)),
451
+ ...this.inlineScripts().map(script => this.#debuggerWorkspaceBinding?.updateLocations(script)),
452
+ ...this.inlineStyles().map(style => this.#cssWorkspaceBinding?.updateLocations(style)),
362
453
  ]);
363
454
  }
364
455
  inlineStyles() {
@@ -421,7 +512,7 @@ class Binding {
421
512
  continue;
422
513
  }
423
514
  scriptRangeMap.set(script, range.rebaseAfterTextEdit(oldRange, newRange));
424
- updatePromises.push(DebuggerWorkspaceBinding.instance().updateLocations(script));
515
+ updatePromises.push(this.#debuggerWorkspaceBinding?.updateLocations(script));
425
516
  }
426
517
  for (const style of styles) {
427
518
  const range = styleSheetRangeMap.get(style) ?? computeStyleSheetRange(style);
@@ -429,7 +520,7 @@ class Binding {
429
520
  continue;
430
521
  }
431
522
  styleSheetRangeMap.set(style, range.rebaseAfterTextEdit(oldRange, newRange));
432
- updatePromises.push(CSSWorkspaceBinding.instance().updateLocations(style));
523
+ updatePromises.push(this.#cssWorkspaceBinding?.updateLocations(style));
433
524
  }
434
525
  await Promise.all(updatePromises);
435
526
  }
@@ -450,8 +541,8 @@ class Binding {
450
541
  dispose() {
451
542
  this.#project.removeUISourceCode(this.#uiSourceCode.url());
452
543
  void Promise.all([
453
- ...this.inlineScripts().map(script => DebuggerWorkspaceBinding.instance().updateLocations(script)),
454
- ...this.inlineStyles().map(style => CSSWorkspaceBinding.instance().updateLocations(style)),
544
+ ...this.inlineScripts().map(script => this.#debuggerWorkspaceBinding?.updateLocations(script)),
545
+ ...this.inlineStyles().map(style => this.#cssWorkspaceBinding?.updateLocations(style)),
455
546
  ]);
456
547
  }
457
548
  firstResource() {
@@ -6,6 +6,7 @@ import * as i18n from '../../core/i18n/i18n.js';
6
6
  import * as Platform from '../../core/platform/platform.js';
7
7
  import * as Root from '../../core/root/root.js';
8
8
  import * as SDK from '../../core/sdk/sdk.js';
9
+ import * as Formatter from '../formatter/formatter.js';
9
10
  import * as TextUtils from '../text_utils/text_utils.js';
10
11
  import * as Workspace from '../workspace/workspace.js';
11
12
  import { ContentProviderBasedProject } from './ContentProviderBasedProject.js';
@@ -125,6 +126,43 @@ export class ResourceScriptMapping {
125
126
  this.addScript(script);
126
127
  }
127
128
  }
129
+ async functionBoundsAtRawLocation(rawLocation) {
130
+ const script = rawLocation.script();
131
+ if (!script) {
132
+ return null;
133
+ }
134
+ const uiSourceCode = this.#scriptToUISourceCode.get(script);
135
+ if (!uiSourceCode) {
136
+ return null;
137
+ }
138
+ const scopeTreeAndText = script ? await SDK.ScopeTreeCache.scopeTreeForScript(script) : null;
139
+ if (!scopeTreeAndText) {
140
+ return null;
141
+ }
142
+ // Find the inner-most scope that maps to the given position.
143
+ const offset = scopeTreeAndText.text.offsetFromPosition(rawLocation.lineNumber, rawLocation.columnNumber);
144
+ const results = [];
145
+ (function walk(nodes) {
146
+ for (const node of nodes) {
147
+ if (!(offset >= node.start && offset < node.end)) {
148
+ continue;
149
+ }
150
+ results.push(node);
151
+ walk(node.children);
152
+ }
153
+ })([scopeTreeAndText.scopeTree]);
154
+ const result = results.findLast(node => node.kind === 2 /* Formatter.FormatterWorkerPool.ScopeKind.FUNCTION */ ||
155
+ node.kind === 4 /* Formatter.FormatterWorkerPool.ScopeKind.ARROW_FUNCTION */);
156
+ if (!result) {
157
+ return null;
158
+ }
159
+ // Map back to positions.
160
+ const startPosition = scopeTreeAndText.text.positionFromOffset(result.start);
161
+ const endPosition = scopeTreeAndText.text.positionFromOffset(result.end);
162
+ const name = ''; // TODO(crbug.com/452333154): update ScopeVariableAnalysis to include function name.
163
+ const range = new TextUtils.TextRange.TextRange(startPosition.lineNumber, startPosition.columnNumber, endPosition.lineNumber, endPosition.columnNumber);
164
+ return new Workspace.UISourceCode.UIFunctionBounds(uiSourceCode, range, name);
165
+ }
128
166
  addScript(script) {
129
167
  // Ignore live edit scripts here.
130
168
  if (script.isLiveEdit() || script.isBreakpointCondition) {
@@ -6,15 +6,16 @@ import * as SDK from '../../core/sdk/sdk.js';
6
6
  import * as TextUtils from '../text_utils/text_utils.js';
7
7
  import * as Workspace from '../workspace/workspace.js';
8
8
  import { ContentProviderBasedProject } from './ContentProviderBasedProject.js';
9
- import { CSSWorkspaceBinding } from './CSSWorkspaceBinding.js';
10
9
  import { NetworkProject } from './NetworkProject.js';
11
10
  export class SASSSourceMapping {
12
11
  #sourceMapManager;
13
12
  #project;
14
13
  #eventListeners;
15
14
  #bindings;
16
- constructor(target, sourceMapManager, workspace) {
15
+ #cssWorkspaceBinding;
16
+ constructor(target, sourceMapManager, workspace, cssWorkspaceBinding) {
17
17
  this.#sourceMapManager = sourceMapManager;
18
+ this.#cssWorkspaceBinding = cssWorkspaceBinding;
18
19
  this.#project = new ContentProviderBasedProject(workspace, 'cssSourceMaps:' + target.id(), Workspace.Workspace.projectTypes.Network, '', false /* isServiceProject */);
19
20
  NetworkProject.setTargetForProject(this.#project, target);
20
21
  this.#bindings = new Map();
@@ -38,7 +39,7 @@ export class SASSSourceMapping {
38
39
  }
39
40
  binding.addSourceMap(sourceMap, header.frameId);
40
41
  }
41
- await CSSWorkspaceBinding.instance().updateLocations(header);
42
+ await this.#cssWorkspaceBinding.updateLocations(header);
42
43
  this.sourceMapAttachedForTest(sourceMap);
43
44
  }
44
45
  async sourceMapDetached(event) {
@@ -54,7 +55,7 @@ export class SASSSourceMapping {
54
55
  }
55
56
  }
56
57
  }
57
- await CSSWorkspaceBinding.instance().updateLocations(header);
58
+ await this.#cssWorkspaceBinding.updateLocations(header);
58
59
  }
59
60
  rawLocationToUILocation(rawLocation) {
60
61
  const header = rawLocation.header();