chrome-devtools-frontend 1.0.1543082 → 1.0.1544076

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 (149) hide show
  1. package/AUTHORS +2 -0
  2. package/front_end/core/common/Gzip.ts +4 -4
  3. package/front_end/core/common/common.ts +0 -2
  4. package/front_end/core/host/AidaClient.ts +10 -7
  5. package/front_end/core/host/DispatchHttpRequestClient.ts +18 -3
  6. package/front_end/core/root/DevToolsContext.ts +60 -0
  7. package/front_end/core/root/Runtime.ts +8 -7
  8. package/front_end/core/root/root.ts +6 -1
  9. package/front_end/core/sdk/CPUThrottlingManager.ts +0 -4
  10. package/front_end/core/sdk/CSSMatchedStyles.ts +7 -9
  11. package/front_end/core/sdk/CSSModel.ts +1 -1
  12. package/front_end/core/sdk/CSSRule.ts +18 -6
  13. package/front_end/core/sdk/ChildTargetManager.ts +2 -2
  14. package/front_end/core/sdk/TargetManager.ts +5 -6
  15. package/front_end/entrypoints/heap_snapshot_worker/HeapSnapshotLoader.ts +2 -0
  16. package/front_end/entrypoints/inspector_main/InspectorMain.ts +1 -13
  17. package/front_end/entrypoints/main/MainImpl.ts +2 -20
  18. package/front_end/foundation/Universe.ts +24 -1
  19. package/front_end/models/ai_assistance/agents/AiAgent.ts +10 -8
  20. package/front_end/models/ai_assistance/agents/PatchAgent.ts +7 -1
  21. package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +0 -5
  22. package/front_end/models/ai_assistance/agents/StylingAgent.ts +4 -8
  23. package/front_end/models/ai_code_completion/AiCodeCompletion.ts +1 -1
  24. package/front_end/models/ai_code_generation/AiCodeGeneration.ts +5 -3
  25. package/front_end/models/bindings/CSSWorkspaceBinding.ts +8 -7
  26. package/front_end/models/bindings/DebuggerWorkspaceBinding.ts +19 -15
  27. package/front_end/models/bindings/ResourceMapping.ts +57 -15
  28. package/front_end/models/live-metrics/LiveMetrics.ts +12 -20
  29. package/front_end/models/trace/handlers/SamplesHandler.ts +64 -6
  30. package/front_end/models/trace/types/TraceEvents.ts +16 -0
  31. package/front_end/models/workspace/IgnoreListManager.ts +10 -9
  32. package/front_end/models/workspace/WorkspaceImpl.ts +5 -10
  33. package/front_end/panels/accessibility/AccessibilityNodeView.ts +6 -2
  34. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +1 -1
  35. package/front_end/panels/ai_assistance/components/ChatView.ts +2 -4
  36. package/front_end/panels/ai_assistance/components/PerformanceAgentMarkdownRenderer.ts +2 -1
  37. package/front_end/panels/animation/AnimationTimeline.ts +6 -6
  38. package/front_end/panels/application/ApplicationPanelSidebar.ts +0 -1
  39. package/front_end/panels/application/OpenedWindowDetailsView.ts +0 -2
  40. package/front_end/panels/application/ServiceWorkersView.ts +0 -2
  41. package/front_end/panels/application/StorageView.ts +0 -1
  42. package/front_end/panels/application/components/FrameDetailsView.ts +468 -447
  43. package/front_end/panels/application/components/ReportsGrid.ts +7 -2
  44. package/front_end/panels/application/components/SharedStorageAccessGrid.ts +5 -3
  45. package/front_end/panels/application/components/TrustTokensView.ts +7 -1
  46. package/front_end/panels/application/preloading/PreloadingView.ts +10 -4
  47. package/front_end/panels/application/preloading/components/PreloadingDisabledInfobar.ts +7 -11
  48. package/front_end/panels/application/preloading/components/UsedPreloadingView.ts +15 -3
  49. package/front_end/panels/browser_debugger/DOMBreakpointsSidebarPane.ts +12 -13
  50. package/front_end/panels/{elements → common}/DOMLinkifier.ts +6 -6
  51. package/front_end/panels/common/common.ts +1 -0
  52. package/front_end/panels/console/ConsoleView.ts +9 -7
  53. package/front_end/panels/console/ConsoleViewMessage.ts +23 -13
  54. package/front_end/panels/css_overview/CSSOverviewCompletedView.ts +2 -1
  55. package/front_end/panels/elements/ElementsTreeElement.ts +3 -1
  56. package/front_end/panels/elements/StylePropertiesSection.ts +52 -15
  57. package/front_end/panels/elements/StylePropertyTreeElement.ts +8 -3
  58. package/front_end/panels/elements/StylesSidebarPane.ts +24 -14
  59. package/front_end/panels/elements/elements-meta.ts +11 -2
  60. package/front_end/panels/elements/elements.ts +0 -3
  61. package/front_end/panels/explain/components/ConsoleInsight.ts +333 -318
  62. package/front_end/panels/issues/AffectedResourcesView.ts +2 -1
  63. package/front_end/panels/lighthouse/LighthouseReportRenderer.ts +2 -1
  64. package/front_end/panels/network/NetworkLogView.ts +1 -1
  65. package/front_end/panels/recorder/RecorderController.ts +7 -1
  66. package/front_end/panels/settings/SettingsScreen.ts +3 -6
  67. package/front_end/panels/settings/components/SyncSection.ts +218 -226
  68. package/front_end/panels/settings/components/syncSection.css +81 -80
  69. package/front_end/panels/sources/AiCodeCompletionPlugin.ts +42 -294
  70. package/front_end/panels/sources/DebuggerPausedMessage.ts +3 -3
  71. package/front_end/panels/sources/DebuggerPlugin.ts +3 -1
  72. package/front_end/panels/sources/ResourceOriginPlugin.ts +7 -3
  73. package/front_end/panels/sources/SourcesPanel.ts +5 -1
  74. package/front_end/panels/timeline/TimelinePanel.ts +0 -21
  75. package/front_end/panels/timeline/TimelineUIUtils.ts +3 -2
  76. package/front_end/panels/timeline/components/LiveMetricsView.ts +7 -4
  77. package/front_end/panels/timeline/components/insights/NodeLink.ts +3 -2
  78. package/front_end/third_party/puppeteer/README.chromium +2 -2
  79. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/HTTPRequest.d.ts +1 -0
  80. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/HTTPRequest.d.ts.map +1 -1
  81. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/HTTPRequest.js +4 -1
  82. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/HTTPRequest.js.map +1 -1
  83. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/NetworkEventManager.d.ts +1 -0
  84. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/NetworkEventManager.d.ts.map +1 -1
  85. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/NetworkEventManager.js +8 -0
  86. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/NetworkEventManager.js.map +1 -1
  87. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/NetworkManager.d.ts.map +1 -1
  88. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/NetworkManager.js +22 -0
  89. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/NetworkManager.js.map +1 -1
  90. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/injected/injected.d.ts +1 -1
  91. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.d.ts +3 -3
  92. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.js +3 -3
  93. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.js.map +1 -1
  94. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/Mutex.d.ts +2 -2
  95. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/version.d.ts +1 -1
  96. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/version.js +1 -1
  97. package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.js +34 -6
  98. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/HTTPRequest.d.ts +1 -0
  99. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/HTTPRequest.d.ts.map +1 -1
  100. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/HTTPRequest.js +4 -1
  101. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/HTTPRequest.js.map +1 -1
  102. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/NetworkEventManager.d.ts +1 -0
  103. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/NetworkEventManager.d.ts.map +1 -1
  104. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/NetworkEventManager.js +8 -0
  105. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/NetworkEventManager.js.map +1 -1
  106. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/NetworkManager.d.ts.map +1 -1
  107. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/NetworkManager.js +22 -0
  108. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/NetworkManager.js.map +1 -1
  109. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.d.ts +3 -3
  110. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.js +3 -3
  111. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.js.map +1 -1
  112. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/version.d.ts +1 -1
  113. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/version.js +1 -1
  114. package/front_end/third_party/puppeteer/package/package.json +2 -2
  115. package/front_end/third_party/puppeteer/package/src/cdp/HTTPRequest.ts +5 -1
  116. package/front_end/third_party/puppeteer/package/src/cdp/NetworkEventManager.ts +16 -1
  117. package/front_end/third_party/puppeteer/package/src/cdp/NetworkManager.ts +28 -0
  118. package/front_end/third_party/puppeteer/package/src/revisions.ts +3 -3
  119. package/front_end/third_party/puppeteer/package/src/util/version.ts +1 -1
  120. package/front_end/ui/components/docs/component_docs.ts +0 -4
  121. package/front_end/ui/components/report_view/ReportView.ts +4 -1
  122. package/front_end/ui/components/text_editor/AiCodeCompletionProvider.ts +8 -5
  123. package/front_end/ui/i18n/i18n.ts +16 -0
  124. package/front_end/ui/legacy/ReportView.ts +0 -5
  125. package/front_end/ui/legacy/TextPrompt.ts +65 -19
  126. package/front_end/ui/legacy/UIUtils.ts +1 -1
  127. package/front_end/ui/legacy/Widget.ts +56 -25
  128. package/front_end/ui/legacy/XLink.ts +0 -2
  129. package/front_end/ui/legacy/components/object_ui/JavaScriptREPL.ts +8 -4
  130. package/front_end/ui/legacy/components/object_ui/ObjectPopoverHelper.ts +3 -1
  131. package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +239 -284
  132. package/front_end/ui/legacy/components/object_ui/RemoteObjectPreviewFormatter.ts +114 -184
  133. package/front_end/ui/legacy/components/utils/Linkifier.ts +1 -1
  134. package/front_end/ui/legacy/inspectorCommon.css +0 -4
  135. package/front_end/ui/{components/docs/theme_colors/basic.ts → legacy/theme_support/ThemeColors.docs.ts} +33 -23
  136. package/mcp/mcp.ts +1 -0
  137. package/package.json +1 -1
  138. package/front_end/core/common/QueryParamHandler.ts +0 -7
  139. package/front_end/ui/components/docs/input/basic.html +0 -31
  140. package/front_end/ui/components/docs/input/basic.ts +0 -12
  141. package/front_end/ui/components/docs/report/basic.html +0 -27
  142. package/front_end/ui/components/docs/report/basic.ts +0 -48
  143. package/front_end/ui/components/docs/theme_colors/basic.html +0 -56
  144. package/front_end/ui/components/docs/toggle_dark_mode.ts +0 -36
  145. package/front_end/ui/components/docs/toggle_fonts.ts +0 -74
  146. package/front_end/ui/components/docs/user_agent_client_hints/basic.html +0 -25
  147. package/front_end/ui/components/docs/user_agent_client_hints/basic.ts +0 -26
  148. package/front_end/ui/components/expandable_list/ExpandableList.docs.ts +0 -30
  149. /package/front_end/panels/{elements → common}/domLinkifier.css +0 -0
@@ -73,6 +73,7 @@ export class AiCodeGeneration {
73
73
  inference_language: inferenceLanguage,
74
74
  temperature: validTemperature(this.#options.temperature),
75
75
  model_id: this.#options.modelId || undefined,
76
+ expect_code_output: true,
76
77
  },
77
78
  metadata: {
78
79
  disable_user_content_logging: !(this.#serverSideLoggingEnabled ?? false),
@@ -139,10 +140,11 @@ export class AiCodeGeneration {
139
140
  Host.userMetrics.actionTaken(Host.UserMetrics.Action.AiCodeGenerationSuggestionAccepted);
140
141
  }
141
142
 
142
- async generateCode(prompt: string, preamble: string, inferenceLanguage?: Host.AidaClient.AidaInferenceLanguage):
143
- Promise<Host.AidaClient.GenerateCodeResponse|null> {
143
+ async generateCode(
144
+ prompt: string, preamble: string, inferenceLanguage?: Host.AidaClient.AidaInferenceLanguage,
145
+ options?: {signal?: AbortSignal}): Promise<Host.AidaClient.GenerateCodeResponse|null> {
144
146
  const request = this.#buildRequest(prompt, preamble, inferenceLanguage);
145
- const response = await this.#aidaClient.generateCode(request);
147
+ const response = await this.#aidaClient.generateCode(request, options);
146
148
 
147
149
  debugLog({request, response});
148
150
 
@@ -4,6 +4,7 @@
4
4
 
5
5
  import * as Common from '../../core/common/common.js';
6
6
  import * as Platform from '../../core/platform/platform.js';
7
+ import * as Root from '../../core/root/root.js';
7
8
  import * as SDK from '../../core/sdk/sdk.js';
8
9
  import type * as Workspace from '../workspace/workspace.js';
9
10
 
@@ -16,15 +17,14 @@ import type {ResourceMapping} from './ResourceMapping.js';
16
17
  import {SASSSourceMapping} from './SASSSourceMapping.js';
17
18
  import {StylesSourceMapping} from './StylesSourceMapping.js';
18
19
 
19
- let cssWorkspaceBindingInstance: CSSWorkspaceBinding|undefined;
20
-
21
20
  export class CSSWorkspaceBinding implements SDK.TargetManager.SDKModelObserver<SDK.CSSModel.CSSModel> {
22
21
  readonly #resourceMapping: ResourceMapping;
23
22
  readonly #modelToInfo: Map<SDK.CSSModel.CSSModel, ModelInfo>;
24
23
  readonly #liveLocationPromises: Set<Promise<unknown>>;
25
24
 
26
- private constructor(resourceMapping: ResourceMapping, targetManager: SDK.TargetManager.TargetManager) {
25
+ constructor(resourceMapping: ResourceMapping, targetManager: SDK.TargetManager.TargetManager) {
27
26
  this.#resourceMapping = resourceMapping;
27
+ this.#resourceMapping.cssWorkspaceBinding = this;
28
28
  this.#modelToInfo = new Map();
29
29
  targetManager.observeModels(SDK.CSSModel.CSSModel, this);
30
30
 
@@ -37,20 +37,21 @@ export class CSSWorkspaceBinding implements SDK.TargetManager.SDKModelObserver<S
37
37
  targetManager: SDK.TargetManager.TargetManager|null,
38
38
  } = {forceNew: null, resourceMapping: null, targetManager: null}): CSSWorkspaceBinding {
39
39
  const {forceNew, resourceMapping, targetManager} = opts;
40
- if (!cssWorkspaceBindingInstance || forceNew) {
40
+ if (forceNew) {
41
41
  if (!resourceMapping || !targetManager) {
42
42
  throw new Error(`Unable to create CSSWorkspaceBinding: resourceMapping and targetManager must be provided: ${
43
43
  new Error().stack}`);
44
44
  }
45
45
 
46
- cssWorkspaceBindingInstance = new CSSWorkspaceBinding(resourceMapping, targetManager);
46
+ Root.DevToolsContext.globalInstance().set(
47
+ CSSWorkspaceBinding, new CSSWorkspaceBinding(resourceMapping, targetManager));
47
48
  }
48
49
 
49
- return cssWorkspaceBindingInstance;
50
+ return Root.DevToolsContext.globalInstance().get(CSSWorkspaceBinding);
50
51
  }
51
52
 
52
53
  static removeInstance(): void {
53
- cssWorkspaceBindingInstance = undefined;
54
+ Root.DevToolsContext.globalInstance().delete(CSSWorkspaceBinding);
54
55
  }
55
56
 
56
57
  get modelToInfo(): Map<SDK.CSSModel.CSSModel, ModelInfo> {
@@ -4,6 +4,7 @@
4
4
 
5
5
  import * as Common from '../../core/common/common.js';
6
6
  import * as Platform from '../../core/platform/platform.js';
7
+ import * as Root from '../../core/root/root.js';
7
8
  import * as SDK from '../../core/sdk/sdk.js';
8
9
  import * as Protocol from '../../generated/protocol.js';
9
10
  import type * as StackTrace from '../stack_trace/stack_trace.js';
@@ -20,20 +21,21 @@ import {NetworkProject} from './NetworkProject.js';
20
21
  import type {ResourceMapping} from './ResourceMapping.js';
21
22
  import {type ResourceScriptFile, ResourceScriptMapping} from './ResourceScriptMapping.js';
22
23
 
23
- let debuggerWorkspaceBindingInstance: DebuggerWorkspaceBinding|undefined;
24
-
25
24
  export class DebuggerWorkspaceBinding implements SDK.TargetManager.SDKModelObserver<SDK.DebuggerModel.DebuggerModel> {
26
25
  readonly resourceMapping: ResourceMapping;
27
26
  readonly #debuggerModelToData: Map<SDK.DebuggerModel.DebuggerModel, ModelData>;
28
27
  readonly #liveLocationPromises: Set<Promise<void|Location|StackTraceTopFrameLocation|null>>;
29
28
  readonly pluginManager: DebuggerLanguagePluginManager;
30
29
  readonly ignoreListManager: Workspace.IgnoreListManager.IgnoreListManager;
30
+ readonly workspace: Workspace.Workspace.WorkspaceImpl;
31
31
 
32
- private constructor(
32
+ constructor(
33
33
  resourceMapping: ResourceMapping, targetManager: SDK.TargetManager.TargetManager,
34
- ignoreListManager: Workspace.IgnoreListManager.IgnoreListManager) {
34
+ ignoreListManager: Workspace.IgnoreListManager.IgnoreListManager, workspace: Workspace.Workspace.WorkspaceImpl) {
35
35
  this.resourceMapping = resourceMapping;
36
+ this.resourceMapping.debuggerWorkspaceBinding = this;
36
37
  this.ignoreListManager = ignoreListManager;
38
+ this.workspace = workspace;
37
39
 
38
40
  this.#debuggerModelToData = new Map();
39
41
  targetManager.addModelListener(
@@ -62,24 +64,27 @@ export class DebuggerWorkspaceBinding implements SDK.TargetManager.SDKModelObser
62
64
  resourceMapping: ResourceMapping|null,
63
65
  targetManager: SDK.TargetManager.TargetManager|null,
64
66
  ignoreListManager: Workspace.IgnoreListManager.IgnoreListManager|null,
65
- } = {forceNew: null, resourceMapping: null, targetManager: null, ignoreListManager: null}): DebuggerWorkspaceBinding {
66
- const {forceNew, resourceMapping, targetManager, ignoreListManager} = opts;
67
- if (!debuggerWorkspaceBindingInstance || forceNew) {
68
- if (!resourceMapping || !targetManager || !ignoreListManager) {
67
+ workspace: Workspace.Workspace.WorkspaceImpl|null,
68
+ } = {forceNew: null, resourceMapping: null, targetManager: null, ignoreListManager: null, workspace: null}):
69
+ DebuggerWorkspaceBinding {
70
+ const {forceNew, resourceMapping, targetManager, ignoreListManager, workspace} = opts;
71
+ if (forceNew) {
72
+ if (!resourceMapping || !targetManager || !ignoreListManager || !workspace) {
69
73
  throw new Error(
70
74
  `Unable to create DebuggerWorkspaceBinding: resourceMapping, targetManager and IgnoreLIstManager must be provided: ${
71
75
  new Error().stack}`);
72
76
  }
73
77
 
74
- debuggerWorkspaceBindingInstance =
75
- new DebuggerWorkspaceBinding(resourceMapping, targetManager, ignoreListManager);
78
+ Root.DevToolsContext.globalInstance().set(
79
+ DebuggerWorkspaceBinding,
80
+ new DebuggerWorkspaceBinding(resourceMapping, targetManager, ignoreListManager, workspace));
76
81
  }
77
82
 
78
- return debuggerWorkspaceBindingInstance;
83
+ return Root.DevToolsContext.globalInstance().get(DebuggerWorkspaceBinding);
79
84
  }
80
85
 
81
86
  static removeInstance(): void {
82
- debuggerWorkspaceBindingInstance = undefined;
87
+ Root.DevToolsContext.globalInstance().delete(DebuggerWorkspaceBinding);
83
88
  }
84
89
 
85
90
  private async computeAutoStepRanges(mode: SDK.DebuggerModel.StepMode, callFrame: SDK.DebuggerModel.CallFrame):
@@ -271,11 +276,10 @@ export class DebuggerWorkspaceBinding implements SDK.TargetManager.SDKModelObser
271
276
  waitForUISourceCodeAdded(url: Platform.DevToolsPath.UrlString, target: SDK.Target.Target):
272
277
  Promise<Workspace.UISourceCode.UISourceCode> {
273
278
  return new Promise(resolve => {
274
- const workspace = Workspace.Workspace.WorkspaceImpl.instance();
275
- const descriptor = workspace.addEventListener(Workspace.Workspace.Events.UISourceCodeAdded, event => {
279
+ const descriptor = this.workspace.addEventListener(Workspace.Workspace.Events.UISourceCodeAdded, event => {
276
280
  const uiSourceCode = event.data;
277
281
  if (uiSourceCode.url() === url && NetworkProject.targetForUISourceCode(uiSourceCode) === target) {
278
- workspace.removeEventListener(Workspace.Workspace.Events.UISourceCodeAdded, descriptor.listener);
282
+ this.workspace.removeEventListener(Workspace.Workspace.Events.UISourceCodeAdded, descriptor.listener);
279
283
  resolve(uiSourceCode);
280
284
  }
281
285
  });
@@ -9,8 +9,8 @@ import * as TextUtils from '../text_utils/text_utils.js';
9
9
  import * as Workspace from '../workspace/workspace.js';
10
10
 
11
11
  import {ContentProviderBasedProject} from './ContentProviderBasedProject.js';
12
- import {CSSWorkspaceBinding} from './CSSWorkspaceBinding.js';
13
- import {DebuggerWorkspaceBinding} from './DebuggerWorkspaceBinding.js';
12
+ import type {CSSWorkspaceBinding} from './CSSWorkspaceBinding.js';
13
+ import type {DebuggerWorkspaceBinding} from './DebuggerWorkspaceBinding.js';
14
14
  import {NetworkProject} from './NetworkProject.js';
15
15
  import {resourceMetadata} from './ResourceUtils.js';
16
16
 
@@ -30,13 +30,46 @@ export class ResourceMapping implements SDK.TargetManager.SDKModelObserver<SDK.R
30
30
  readonly workspace: Workspace.Workspace.WorkspaceImpl;
31
31
  readonly #modelToInfo = new Map<SDK.ResourceTreeModel.ResourceTreeModel, ModelInfo>();
32
32
 
33
+ #debuggerWorkspaceBinding: DebuggerWorkspaceBinding|null = null;
34
+ #cssWorkspaceBinding: CSSWorkspaceBinding|null = null;
35
+
33
36
  constructor(targetManager: SDK.TargetManager.TargetManager, workspace: Workspace.Workspace.WorkspaceImpl) {
34
37
  this.workspace = workspace;
35
38
  targetManager.observeModels(SDK.ResourceTreeModel.ResourceTreeModel, this);
36
39
  }
37
40
 
41
+ get debuggerWorkspaceBinding(): DebuggerWorkspaceBinding|null {
42
+ // TODO(crbug.com/458180550): Throw when this.#debuggerWorkspaceBinding is null and never return null.
43
+ // The only reason we don't throw and return an instance unconditionally
44
+ // is that unit tests often don't set-up both the *WorkspaceBindings.
45
+ return this.#debuggerWorkspaceBinding;
46
+ }
47
+
48
+ /* {@link DebuggerWorkspaceBinding} and ResourceMapping form a cycle so we can't wire it up at ctor time. */
49
+ set debuggerWorkspaceBinding(debuggerWorkspaceBinding: DebuggerWorkspaceBinding) {
50
+ if (this.#debuggerWorkspaceBinding) {
51
+ throw new Error('DebuggerWorkspaceBinding already set');
52
+ }
53
+ this.#debuggerWorkspaceBinding = debuggerWorkspaceBinding;
54
+ }
55
+
56
+ get cssWorkspaceBinding(): CSSWorkspaceBinding|null {
57
+ // TODO(crbug.com/458180550): Throw when this.#cssWorkspaceBinding is null and never return null.
58
+ // The only reason we don't throw and return an instance unconditionally
59
+ // is that unit tests often don't set-up both the *WorkspaceBindings.
60
+ return this.#cssWorkspaceBinding;
61
+ }
62
+
63
+ /* {@link CSSWorkspaceBinding} and ResourceMapping form a cycle so we can't wire it up at ctor time. */
64
+ set cssWorkspaceBinding(cssWorkspaceBinding: CSSWorkspaceBinding) {
65
+ if (this.#cssWorkspaceBinding) {
66
+ throw new Error('CSSWorkspaceBinding already set');
67
+ }
68
+ this.#cssWorkspaceBinding = cssWorkspaceBinding;
69
+ }
70
+
38
71
  modelAdded(resourceTreeModel: SDK.ResourceTreeModel.ResourceTreeModel): void {
39
- const info = new ModelInfo(this.workspace, resourceTreeModel);
72
+ const info = new ModelInfo(this, resourceTreeModel);
40
73
  this.#modelToInfo.set(resourceTreeModel, info);
41
74
  }
42
75
 
@@ -250,11 +283,13 @@ class ModelInfo {
250
283
  readonly #bindings = new Map<string, Binding>();
251
284
  readonly #cssModel: SDK.CSSModel.CSSModel;
252
285
  readonly #eventListeners: Common.EventTarget.EventDescriptor[];
253
- constructor(
254
- workspace: Workspace.Workspace.WorkspaceImpl, resourceTreeModel: SDK.ResourceTreeModel.ResourceTreeModel) {
286
+ readonly resourceMapping: ResourceMapping;
287
+
288
+ constructor(resourceMapping: ResourceMapping, resourceTreeModel: SDK.ResourceTreeModel.ResourceTreeModel) {
255
289
  const target = resourceTreeModel.target();
290
+ this.resourceMapping = resourceMapping;
256
291
  this.project = new ContentProviderBasedProject(
257
- workspace, 'resources:' + target.id(), Workspace.Workspace.projectTypes.Network, '',
292
+ resourceMapping.workspace, 'resources:' + target.id(), Workspace.Workspace.projectTypes.Network, '',
258
293
  false /* isServiceProject */);
259
294
  NetworkProject.setTargetForProject(this.project, target);
260
295
 
@@ -332,7 +367,7 @@ class ModelInfo {
332
367
 
333
368
  let binding = this.#bindings.get(resource.url);
334
369
  if (!binding) {
335
- binding = new Binding(this.project, resource);
370
+ binding = new Binding(this, resource);
336
371
  this.#bindings.set(resource.url, binding);
337
372
  } else {
338
373
  binding.addResource(resource);
@@ -396,9 +431,16 @@ class Binding implements TextUtils.ContentProvider.ContentProvider {
396
431
  stylesheet: SDK.CSSStyleSheetHeader.CSSStyleSheetHeader,
397
432
  edit: SDK.CSSModel.Edit|null,
398
433
  }> = [];
399
- constructor(project: ContentProviderBasedProject, resource: SDK.Resource.Resource) {
434
+
435
+ readonly #debuggerWorkspaceBinding: DebuggerWorkspaceBinding|null;
436
+ readonly #cssWorkspaceBinding: CSSWorkspaceBinding|null;
437
+
438
+ constructor(modelInfo: ModelInfo, resource: SDK.Resource.Resource) {
400
439
  this.resources = new Set([resource]);
401
- this.#project = project;
440
+ this.#project = modelInfo.project;
441
+ this.#debuggerWorkspaceBinding = modelInfo.resourceMapping.debuggerWorkspaceBinding;
442
+ this.#cssWorkspaceBinding = modelInfo.resourceMapping.cssWorkspaceBinding;
443
+
402
444
  this.#uiSourceCode = this.#project.createUISourceCode(resource.url, resource.contentType());
403
445
  boundUISourceCodes.add(this.#uiSourceCode);
404
446
  if (resource.frameId) {
@@ -407,8 +449,8 @@ class Binding implements TextUtils.ContentProvider.ContentProvider {
407
449
  this.#project.addUISourceCodeWithProvider(this.#uiSourceCode, this, resourceMetadata(resource), resource.mimeType);
408
450
 
409
451
  void Promise.all([
410
- ...this.inlineScripts().map(script => DebuggerWorkspaceBinding.instance().updateLocations(script)),
411
- ...this.inlineStyles().map(style => CSSWorkspaceBinding.instance().updateLocations(style)),
452
+ ...this.inlineScripts().map(script => this.#debuggerWorkspaceBinding?.updateLocations(script)),
453
+ ...this.inlineStyles().map(style => this.#cssWorkspaceBinding?.updateLocations(style)),
412
454
  ]);
413
455
  }
414
456
 
@@ -478,7 +520,7 @@ class Binding implements TextUtils.ContentProvider.ContentProvider {
478
520
  continue;
479
521
  }
480
522
  scriptRangeMap.set(script, range.rebaseAfterTextEdit(oldRange, newRange));
481
- updatePromises.push(DebuggerWorkspaceBinding.instance().updateLocations(script));
523
+ updatePromises.push(this.#debuggerWorkspaceBinding?.updateLocations(script));
482
524
  }
483
525
  for (const style of styles) {
484
526
  const range = styleSheetRangeMap.get(style) ?? computeStyleSheetRange(style);
@@ -486,7 +528,7 @@ class Binding implements TextUtils.ContentProvider.ContentProvider {
486
528
  continue;
487
529
  }
488
530
  styleSheetRangeMap.set(style, range.rebaseAfterTextEdit(oldRange, newRange));
489
- updatePromises.push(CSSWorkspaceBinding.instance().updateLocations(style));
531
+ updatePromises.push(this.#cssWorkspaceBinding?.updateLocations(style));
490
532
  }
491
533
  await Promise.all(updatePromises);
492
534
  }
@@ -510,8 +552,8 @@ class Binding implements TextUtils.ContentProvider.ContentProvider {
510
552
  dispose(): void {
511
553
  this.#project.removeUISourceCode(this.#uiSourceCode.url());
512
554
  void Promise.all([
513
- ...this.inlineScripts().map(script => DebuggerWorkspaceBinding.instance().updateLocations(script)),
514
- ...this.inlineStyles().map(style => CSSWorkspaceBinding.instance().updateLocations(style)),
555
+ ...this.inlineScripts().map(script => this.#debuggerWorkspaceBinding?.updateLocations(script)),
556
+ ...this.inlineStyles().map(style => this.#cssWorkspaceBinding?.updateLocations(style)),
515
557
  ]);
516
558
  }
517
559
 
@@ -163,7 +163,8 @@ export class LiveMetrics extends Common.ObjectWrapper.ObjectWrapper<EventTypes>
163
163
  * DOM nodes can't be sent over a runtime binding, so we have to retrieve
164
164
  * them separately.
165
165
  */
166
- async #resolveNodeRef(index: number, executionContextId: Protocol.Runtime.ExecutionContextId): Promise<NodeRef|null> {
166
+ async #resolveNodeRef(index: number, executionContextId: Protocol.Runtime.ExecutionContextId):
167
+ Promise<SDK.DOMModel.DOMNode|null> {
167
168
  if (!this.#target) {
168
169
  return null;
169
170
  }
@@ -195,8 +196,7 @@ export class LiveMetrics extends Common.ObjectWrapper.ObjectWrapper<EventTypes>
195
196
  return null;
196
197
  }
197
198
 
198
- const link = await Common.Linkifier.Linkifier.linkify(node);
199
- return {node, link};
199
+ return node;
200
200
  } catch {
201
201
  return null;
202
202
  } finally {
@@ -236,26 +236,23 @@ export class LiveMetrics extends Common.ObjectWrapper.ObjectWrapper<EventTypes>
236
236
  ...this.#layoutShifts.flatMap(shift => shift.affectedNodeRefs),
237
237
  ].filter(nodeRef => !!nodeRef);
238
238
 
239
- const idsToRefresh = new Set(toRefresh.map(nodeRef => nodeRef.node.backendNodeId()));
239
+ const idsToRefresh = new Set(toRefresh.map(nodeRef => nodeRef.backendNodeId()));
240
240
  const nodes = await domModel.pushNodesByBackendIdsToFrontend(idsToRefresh);
241
241
  if (!nodes) {
242
242
  return;
243
243
  }
244
244
 
245
- const allPromises = toRefresh.map(async nodeRef => {
246
- const refreshedNode = nodes.get(nodeRef.node.backendNodeId());
245
+ for (let i = 0; i < toRefresh.length; i++) {
246
+ const refreshedNode = nodes.get(toRefresh[i].backendNodeId());
247
247
 
248
248
  // It is possible for the refreshed node to be undefined even though it was defined previously.
249
249
  // We should keep the affected nodes consistent from the user perspective, so we will just keep the stale node instead of removing it.
250
250
  if (!refreshedNode) {
251
- return;
251
+ continue;
252
252
  }
253
253
 
254
- nodeRef.node = refreshedNode;
255
- nodeRef.link = await Common.Linkifier.Linkifier.linkify(refreshedNode);
256
- });
257
-
258
- await Promise.all(allPromises);
254
+ toRefresh[i] = refreshedNode;
255
+ }
259
256
 
260
257
  this.#sendStatusUpdate();
261
258
  }
@@ -599,14 +596,9 @@ export interface MetricValue {
599
596
  warnings?: string[];
600
597
  }
601
598
 
602
- export interface NodeRef {
603
- node: SDK.DOMModel.DOMNode;
604
- link: Node;
605
- }
606
-
607
599
  export interface LcpValue extends MetricValue {
608
600
  phases: Spec.LcpPhases;
609
- nodeRef?: NodeRef;
601
+ nodeRef?: SDK.DOMModel.DOMNode;
610
602
  }
611
603
 
612
604
  export interface InpValue extends MetricValue {
@@ -621,7 +613,7 @@ export interface ClsValue extends MetricValue {
621
613
  export interface LayoutShift {
622
614
  score: number;
623
615
  uniqueLayoutShiftId: Spec.UniqueLayoutShiftId;
624
- affectedNodeRefs: NodeRef[];
616
+ affectedNodeRefs: SDK.DOMModel.DOMNode[];
625
617
  }
626
618
 
627
619
  export interface Interaction {
@@ -633,7 +625,7 @@ export interface Interaction {
633
625
  nextPaintTime: number;
634
626
  phases: Spec.InpPhases;
635
627
  longAnimationFrameTimings: Spec.PerformanceLongAnimationFrameTimingJSON[];
636
- nodeRef?: NodeRef;
628
+ nodeRef?: SDK.DOMModel.DOMNode;
637
629
  }
638
630
 
639
631
  export interface StatusEvent {
@@ -22,25 +22,73 @@ let entryToNode = new Map<Types.Events.Event, Helpers.TreeHelpers.TraceEntryNode
22
22
  // events matched by thread id.
23
23
  let preprocessedData = new Map<Types.Events.ProcessID, Map<Types.Events.ProfileID, PreprocessedData>>();
24
24
 
25
+ /**
26
+ * Profile source selection priority when multiple profiles exist for the same thread.
27
+ *
28
+ * Profile sources and their typical scenarios:
29
+ * - 'Internal': Browser-initiated profiling performance panel traces.
30
+ * This is the profiling mechanism when users click "Record" in the Devtools UI.
31
+ * - 'Inspector': User-initiated via console.profile()/profileEnd() calls.
32
+ * Represents explicit developer intent to profile specific code.
33
+ * - 'SelfProfiling': Page-initiated via JS Self-Profiling API.
34
+ * Lower signal vs the two above; treated as fallback.
35
+ *
36
+ * Selection strategy:
37
+ * - CPU Profile mode: Prefer 'Inspector' (explicit user request).
38
+ * - Performance trace: Prefer 'Internal' (integrated timeline context), then 'Inspector'.
39
+ * - Sources not in the priority list (including 'SelfProfiling') act as fallbacks.
40
+ * When no priority source matches, the first candidate profile is selected.
41
+ */
42
+ const PROFILE_SOURCES_BY_PRIORITY = {
43
+ cpuProfile: ['Inspector'] as Types.Events.ProfileSource[],
44
+ performanceTrace: ['Internal', 'Inspector'] as Types.Events.ProfileSource[],
45
+ };
46
+
25
47
  function parseCPUProfileData(parseOptions: Types.Configuration.ParseOptions): void {
48
+ const priorityList =
49
+ parseOptions.isCPUProfile ? PROFILE_SOURCES_BY_PRIORITY.cpuProfile : PROFILE_SOURCES_BY_PRIORITY.performanceTrace;
50
+
26
51
  for (const [processId, profiles] of preprocessedData) {
52
+ const profilesByThread =
53
+ new Map<Types.Events.ThreadID, Array<{id: Types.Events.ProfileID, data: PreprocessedData}>>();
27
54
  for (const [profileId, preProcessedData] of profiles) {
28
55
  const threadId = preProcessedData.threadId;
29
- if (!preProcessedData.rawProfile.nodes.length || threadId === undefined) {
56
+ if (threadId === undefined) {
57
+ continue;
58
+ }
59
+ const listForThread = Platform.MapUtilities.getWithDefault(profilesByThread, threadId, () => []);
60
+ listForThread.push({id: profileId, data: preProcessedData});
61
+ }
62
+
63
+ for (const [threadId, candidates] of profilesByThread) {
64
+ if (!candidates.length) {
65
+ continue;
66
+ }
67
+ let chosen = candidates[0];
68
+ for (const source of priorityList) {
69
+ const match = candidates.find(p => p.data.source === source);
70
+ if (match) {
71
+ chosen = match;
72
+ break;
73
+ }
74
+ }
75
+ const chosenData = chosen.data;
76
+ if (!chosenData.rawProfile.nodes.length) {
30
77
  continue;
31
78
  }
32
79
  const indexStack: number[] = [];
33
80
 
34
- const profileModel = new CPUProfile.CPUProfileDataModel.CPUProfileDataModel(preProcessedData.rawProfile);
81
+ const profileModel = new CPUProfile.CPUProfileDataModel.CPUProfileDataModel(chosenData.rawProfile);
35
82
  const profileTree = Helpers.TreeHelpers.makeEmptyTraceEntryTree();
36
83
  profileTree.maxDepth = profileModel.maxDepth;
37
84
 
85
+ const selectedProfileId = chosen.id;
38
86
  const finalizedData: ProfileData = {
39
- rawProfile: preProcessedData.rawProfile,
87
+ rawProfile: chosenData.rawProfile,
40
88
  parsedProfile: profileModel,
41
89
  profileCalls: [],
42
90
  profileTree,
43
- profileId,
91
+ profileId: selectedProfileId,
44
92
  };
45
93
  const dataByThread = Platform.MapUtilities.getWithDefault(profilesInProcess, processId, () => new Map());
46
94
  dataByThread.set(threadId, finalizedData);
@@ -62,7 +110,8 @@ function parseCPUProfileData(parseOptions: Types.Configuration.ParseOptions): vo
62
110
  const ts = Helpers.Timing.milliToMicro(Types.Timing.Milli(timeStampMilliseconds));
63
111
  const nodeId = node.id as Helpers.TreeHelpers.TraceEntryNodeId;
64
112
 
65
- const profileCall = Helpers.Trace.makeProfileCall(node, profileId, sampleIndex, ts, processId, threadId);
113
+ const profileCall =
114
+ Helpers.Trace.makeProfileCall(node, selectedProfileId, sampleIndex, ts, processId, threadId);
66
115
  finalizedData.profileCalls.push(profileCall);
67
116
  indexStack.push(finalizedData.profileCalls.length - 1);
68
117
  const traceEntryNode = Helpers.TreeHelpers.makeEmptyTraceEntryNode(profileCall, nodeId);
@@ -83,7 +132,7 @@ function parseCPUProfileData(parseOptions: Types.Configuration.ParseOptions): vo
83
132
  }
84
133
  const {callFrame, ts, pid, tid} = profileCall;
85
134
  const traceEntryNode = entryToNode.get(profileCall);
86
- if (callFrame === undefined || ts === undefined || pid === undefined || profileId === undefined ||
135
+ if (callFrame === undefined || ts === undefined || pid === undefined || selectedProfileId === undefined ||
87
136
  tid === undefined || traceEntryNode === undefined) {
88
137
  return;
89
138
  }
@@ -138,6 +187,7 @@ export function handleEvent(event: Types.Events.Event): void {
138
187
  const profileData = getOrCreatePreProcessedData(event.pid, event.id);
139
188
  profileData.rawProfile.startTime = event.ts;
140
189
  profileData.threadId = event.tid;
190
+ assignProfileSourceIfKnown(profileData, event.args?.data?.source);
141
191
  return;
142
192
  }
143
193
  if (Types.Events.isProfileChunk(event)) {
@@ -186,6 +236,7 @@ export function handleEvent(event: Types.Events.Event): void {
186
236
  const timeDeltas: number[] = cdpProfile.timeDeltas;
187
237
  cdpProfile.endTime = timeDeltas.reduce((x, y) => x + y, cdpProfile.startTime);
188
238
  }
239
+ assignProfileSourceIfKnown(profileData, event.args?.data?.source);
189
240
  return;
190
241
  }
191
242
  }
@@ -194,6 +245,12 @@ export async function finalize(parseOptions: Types.Configuration.ParseOptions =
194
245
  parseCPUProfileData(parseOptions);
195
246
  }
196
247
 
248
+ function assignProfileSourceIfKnown(profileData: PreprocessedData, source: unknown): void {
249
+ if (Types.Events.VALID_PROFILE_SOURCES.includes(source as Types.Events.ProfileSource)) {
250
+ profileData.source = source as Types.Events.ProfileSource;
251
+ }
252
+ }
253
+
197
254
  export function data(): SamplesHandlerData {
198
255
  return {
199
256
  profilesInProcess,
@@ -251,6 +308,7 @@ interface PreprocessedData {
251
308
  rawProfile: CPUProfile.CPUProfileDataModel.ExtendedProfile;
252
309
  profileId: Types.Events.ProfileID;
253
310
  threadId?: Types.Events.ThreadID;
311
+ source?: Types.Events.ProfileSource;
254
312
  }
255
313
 
256
314
  /**
@@ -161,6 +161,7 @@ export interface Profile extends Sample {
161
161
  args: Args&{
162
162
  data: ArgsData & {
163
163
  startTime: Micro,
164
+ source?: ProfileSource,
164
165
  },
165
166
  };
166
167
  }
@@ -174,6 +175,7 @@ export interface ProfileChunk extends Sample {
174
175
  cpuProfile?: PartialProfile,
175
176
  timeDeltas?: Micro[],
176
177
  lines?: Micro[],
178
+ source?: ProfileSource,
177
179
  },
178
180
  };
179
181
  }
@@ -191,6 +193,20 @@ export interface PartialProfile {
191
193
  /* eslint-enable @typescript-eslint/naming-convention */
192
194
  }
193
195
 
196
+ /**
197
+ * Source of profile data, used to select the most relevant profile when
198
+ * multiple profiles exist for the same thread.
199
+ *
200
+ * - 'Inspector': User-initiated via console.profile()/profileEnd().
201
+ * - 'Internal': Browser-initiated during performance traces.
202
+ * - 'SelfProfiling': Page-initiated via JS Self-Profiling API.
203
+ *
204
+ * Selection priority (see PROFILE_SOURCES_BY_PRIORITY in SamplesHandler.ts).
205
+ */
206
+ export type ProfileSource = 'Inspector'|'SelfProfiling'|'Internal';
207
+
208
+ export const VALID_PROFILE_SOURCES: readonly ProfileSource[] = ['Inspector', 'SelfProfiling', 'Internal'] as const;
209
+
194
210
  export interface PartialNode {
195
211
  callFrame: CallFrame;
196
212
  id: CallFrameID;
@@ -5,6 +5,7 @@
5
5
  import * as Common from '../../core/common/common.js';
6
6
  import * as i18n from '../../core/i18n/i18n.js';
7
7
  import * as Platform from '../../core/platform/platform.js';
8
+ import * as Root from '../../core/root/root.js';
8
9
  import * as SDK from '../../core/sdk/sdk.js';
9
10
 
10
11
  import type {UISourceCode} from './UISourceCode.js';
@@ -40,8 +41,6 @@ const UIStrings = {
40
41
  const str_ = i18n.i18n.registerUIStrings('models/workspace/IgnoreListManager.ts', UIStrings);
41
42
  const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
42
43
 
43
- let ignoreListManagerInstance: IgnoreListManager|undefined;
44
-
45
44
  export interface IgnoreListGeneralRules {
46
45
  isContentScript?: boolean;
47
46
  isKnownThirdParty?: boolean;
@@ -57,7 +56,7 @@ export class IgnoreListManager extends Common.ObjectWrapper.ObjectWrapper<EventT
57
56
  readonly #isIgnoreListedURLCache = new Map<string, boolean>();
58
57
  readonly #contentScriptExecutionContexts = new Set<string>();
59
58
 
60
- private constructor(settings: Common.Settings.Settings, targetManager: SDK.TargetManager.TargetManager) {
59
+ constructor(settings: Common.Settings.Settings, targetManager: SDK.TargetManager.TargetManager) {
61
60
  super();
62
61
  this.#settings = settings;
63
62
  this.#targetManager = targetManager;
@@ -89,17 +88,19 @@ export class IgnoreListManager extends Common.ObjectWrapper.ObjectWrapper<EventT
89
88
  forceNew: null,
90
89
  }): IgnoreListManager {
91
90
  const {forceNew} = opts;
92
- if (!ignoreListManagerInstance || forceNew) {
93
- ignoreListManagerInstance = new IgnoreListManager(
94
- opts.settings ?? Common.Settings.Settings.instance(),
95
- opts.targetManager ?? SDK.TargetManager.TargetManager.instance());
91
+ if (forceNew) {
92
+ Root.DevToolsContext.globalInstance().set(
93
+ IgnoreListManager,
94
+ new IgnoreListManager(
95
+ opts.settings ?? Common.Settings.Settings.instance(),
96
+ opts.targetManager ?? SDK.TargetManager.TargetManager.instance()));
96
97
  }
97
98
 
98
- return ignoreListManagerInstance;
99
+ return Root.DevToolsContext.globalInstance().get(IgnoreListManager);
99
100
  }
100
101
 
101
102
  static removeInstance(): void {
102
- ignoreListManagerInstance = undefined;
103
+ Root.DevToolsContext.globalInstance().delete(IgnoreListManager);
103
104
  }
104
105
 
105
106
  addChangeListener(listener: () => void): void {
@@ -4,6 +4,7 @@
4
4
 
5
5
  import * as Common from '../../core/common/common.js';
6
6
  import type * as Platform from '../../core/platform/platform.js';
7
+ import * as Root from '../../core/root/root.js';
7
8
  import type * as TextUtils from '../text_utils/text_utils.js';
8
9
 
9
10
  import type {SearchConfig} from './SearchConfig.js';
@@ -183,27 +184,21 @@ export abstract class ProjectStore implements Project {
183
184
  progress: Common.Progress.Progress): Promise<Map<UISourceCode, TextUtils.ContentProvider.SearchMatch[]|null>>;
184
185
  }
185
186
 
186
- let workspaceInstance: WorkspaceImpl|undefined;
187
-
188
187
  export class WorkspaceImpl extends Common.ObjectWrapper.ObjectWrapper<EventTypes> {
189
188
  #projects = new Map<string, Project>();
190
189
  #hasResourceContentTrackingExtensions = false;
191
190
 
192
- private constructor() {
193
- super();
194
- }
195
-
196
191
  static instance(opts: {forceNew: boolean|null} = {forceNew: null}): WorkspaceImpl {
197
192
  const {forceNew} = opts;
198
- if (!workspaceInstance || forceNew) {
199
- workspaceInstance = new WorkspaceImpl();
193
+ if (!Root.DevToolsContext.globalInstance().has(WorkspaceImpl) || forceNew) {
194
+ Root.DevToolsContext.globalInstance().set(WorkspaceImpl, new WorkspaceImpl());
200
195
  }
201
196
 
202
- return workspaceInstance;
197
+ return Root.DevToolsContext.globalInstance().get(WorkspaceImpl);
203
198
  }
204
199
 
205
200
  static removeInstance(): void {
206
- workspaceInstance = undefined;
201
+ Root.DevToolsContext.globalInstance().delete(WorkspaceImpl);
207
202
  }
208
203
 
209
204
  uiSourceCode(projectId: string, url: Platform.DevToolsPath.UrlString): UISourceCode|null {