chrome-devtools-frontend 1.0.1543082 → 1.0.1543472

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 (48) hide show
  1. package/AUTHORS +1 -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/root/DevToolsContext.ts +60 -0
  5. package/front_end/core/root/root.ts +6 -1
  6. package/front_end/core/sdk/TargetManager.ts +5 -6
  7. package/front_end/entrypoints/inspector_main/InspectorMain.ts +1 -13
  8. package/front_end/entrypoints/main/MainImpl.ts +3 -5
  9. package/front_end/foundation/Universe.ts +13 -1
  10. package/front_end/models/bindings/DebuggerWorkspaceBinding.ts +11 -8
  11. package/front_end/models/trace/handlers/SamplesHandler.ts +64 -6
  12. package/front_end/models/trace/types/TraceEvents.ts +16 -0
  13. package/front_end/models/workspace/IgnoreListManager.ts +10 -9
  14. package/front_end/models/workspace/WorkspaceImpl.ts +5 -10
  15. package/front_end/panels/application/ApplicationPanelSidebar.ts +0 -1
  16. package/front_end/panels/application/OpenedWindowDetailsView.ts +0 -2
  17. package/front_end/panels/application/ServiceWorkersView.ts +0 -2
  18. package/front_end/panels/application/StorageView.ts +0 -1
  19. package/front_end/panels/application/components/FrameDetailsView.ts +468 -447
  20. package/front_end/panels/console/ConsoleView.ts +9 -7
  21. package/front_end/panels/console/ConsoleViewMessage.ts +19 -9
  22. package/front_end/panels/explain/components/ConsoleInsight.ts +314 -310
  23. package/front_end/panels/settings/SettingsScreen.ts +3 -6
  24. package/front_end/panels/settings/components/SyncSection.ts +218 -226
  25. package/front_end/panels/settings/components/syncSection.css +81 -80
  26. package/front_end/panels/sources/DebuggerPlugin.ts +3 -1
  27. package/front_end/panels/sources/ResourceOriginPlugin.ts +7 -3
  28. package/front_end/panels/timeline/TimelinePanel.ts +0 -21
  29. package/front_end/ui/components/docs/component_docs.ts +0 -4
  30. package/front_end/ui/components/report_view/ReportView.ts +4 -1
  31. package/front_end/ui/legacy/ReportView.ts +0 -5
  32. package/front_end/ui/legacy/TextPrompt.ts +65 -19
  33. package/front_end/ui/legacy/components/object_ui/JavaScriptREPL.ts +8 -4
  34. package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +90 -92
  35. package/front_end/ui/legacy/components/object_ui/RemoteObjectPreviewFormatter.ts +114 -184
  36. package/front_end/ui/legacy/components/utils/Linkifier.ts +1 -1
  37. package/front_end/ui/{components/docs/theme_colors/basic.ts → legacy/theme_support/ThemeColors.docs.ts} +33 -23
  38. package/package.json +1 -1
  39. package/front_end/core/common/QueryParamHandler.ts +0 -7
  40. package/front_end/ui/components/docs/input/basic.html +0 -31
  41. package/front_end/ui/components/docs/input/basic.ts +0 -12
  42. package/front_end/ui/components/docs/report/basic.html +0 -27
  43. package/front_end/ui/components/docs/report/basic.ts +0 -48
  44. package/front_end/ui/components/docs/theme_colors/basic.html +0 -56
  45. package/front_end/ui/components/docs/toggle_dark_mode.ts +0 -36
  46. package/front_end/ui/components/docs/toggle_fonts.ts +0 -74
  47. package/front_end/ui/components/docs/user_agent_client_hints/basic.html +0 -25
  48. package/front_end/ui/components/docs/user_agent_client_hints/basic.ts +0 -26
package/AUTHORS CHANGED
@@ -68,6 +68,7 @@ Michael Rienstra <mrienstra@gmail.com>
68
68
  Mostafa Aboalkasim <mostafa.aboalkasim.offical@gmail.com>
69
69
  Muhammad Mahad <mahadtxt@gmail.com>
70
70
  Naoto Ono <onoto1998@gmail.com>
71
+ Nourhan Hasan <nourhan.m.hasan@gmail.com>
71
72
  Paras Awasthi <awasthiparas6@gmail.com>
72
73
  Paul Fisher <paul@pfish.zone>
73
74
  Peng Zhou <zhoupeng.1996@bytedance.com>
@@ -5,7 +5,7 @@
5
5
  /**
6
6
  * Quickly determine if gzipped, by seeing if the first 3 bytes of the file header match the gzip signature
7
7
  */
8
- export function isGzip(ab: ArrayBuffer): boolean {
8
+ export function isGzip(ab: ArrayBufferLike): boolean {
9
9
  const buf = new Uint8Array(ab);
10
10
  if (!buf || buf.length < 3) {
11
11
  return false;
@@ -15,7 +15,7 @@ export function isGzip(ab: ArrayBuffer): boolean {
15
15
  }
16
16
 
17
17
  /** Decode a gzipped _or_ plain text ArrayBuffer to a decoded string */
18
- export async function arrayBufferToString(ab: ArrayBuffer): Promise<string> {
18
+ export async function arrayBufferToString(ab: ArrayBufferLike): Promise<string> {
19
19
  if (isGzip(ab)) {
20
20
  return await decompress(ab);
21
21
  }
@@ -37,7 +37,7 @@ export async function fileToString(file: File): Promise<string> {
37
37
  * Decompress a gzipped ArrayBuffer to a string.
38
38
  * Consider using `arrayBufferToString` instead, which can handle both gzipped and plain text buffers.
39
39
  */
40
- export async function decompress(gzippedBuffer: ArrayBuffer): Promise<string> {
40
+ export async function decompress(gzippedBuffer: ArrayBufferLike): Promise<string> {
41
41
  const buffer = await gzipCodec(gzippedBuffer, new DecompressionStream('gzip'));
42
42
  const str = new TextDecoder('utf-8').decode(buffer);
43
43
  return str;
@@ -50,7 +50,7 @@ export async function compress(str: string): Promise<ArrayBuffer> {
50
50
 
51
51
  /** Private coder/decoder **/
52
52
  async function gzipCodec(
53
- buffer: Uint8Array<ArrayBufferLike>|ArrayBuffer,
53
+ buffer: Uint8Array<ArrayBufferLike>|ArrayBufferLike,
54
54
  codecStream: CompressionStream|DecompressionStream): Promise<ArrayBuffer> {
55
55
  const readable = new ReadableStream({
56
56
  start(controller) {
@@ -21,7 +21,6 @@ import * as Mutex from './Mutex.js';
21
21
  import * as ObjectWrapper from './Object.js';
22
22
  import * as ParsedURL from './ParsedURL.js';
23
23
  import * as Progress from './Progress.js';
24
- import * as QueryParamHandler from './QueryParamHandler.js';
25
24
  import * as ResolverBase from './ResolverBase.js';
26
25
  import * as ResourceType from './ResourceType.js';
27
26
  import * as ReturnToPanel from './ReturnToPanel.js';
@@ -63,7 +62,6 @@ export {
63
62
  ObjectWrapper,
64
63
  ParsedURL,
65
64
  Progress,
66
- QueryParamHandler,
67
65
  ResolverBase,
68
66
  ResourceType,
69
67
  ReturnToPanel,
@@ -0,0 +1,60 @@
1
+ // Copyright 2025 The Chromium Authors
2
+ // Use of this source code is governed by a BSD-style license that can be
3
+ // found in the LICENSE file.
4
+
5
+ export type ConstructorT<T> = new (...args: any[]) => T;
6
+
7
+ /**
8
+ * Container for singletons scoped to a single DevTools universe.
9
+ */
10
+ export class DevToolsContext {
11
+ readonly #instances = new Map<ConstructorT<unknown>, unknown>();
12
+
13
+ get<T>(ctor: ConstructorT<T>): T {
14
+ const instance = this.#instances.get(ctor) as T | undefined;
15
+ if (!instance) {
16
+ throw new Error(`No instance for ${ctor.name}. Ensure the bootstrapper creates it.`);
17
+ }
18
+ return instance;
19
+ }
20
+
21
+ /** @deprecated Should only be used by existing `instance` accessors. */
22
+ has<T>(ctor: ConstructorT<T>): boolean {
23
+ return this.#instances.has(ctor);
24
+ }
25
+
26
+ /**
27
+ * @deprecated Should only be used by existing `instance` accessors and the bootstrapper.
28
+ * Exists on the public interface only for migration purposes for now.
29
+ */
30
+ set<T>(ctor: ConstructorT<T>, instance: T): void {
31
+ // TODO(crbug.com/458180550): We need to throw here if an instance was already set!
32
+ this.#instances.set(ctor, instance);
33
+ }
34
+
35
+ /** @deprecated Should only be used by existing `removeInstance` static methods. */
36
+ delete<T>(ctor: ConstructorT<T>): void {
37
+ this.#instances.delete(ctor);
38
+ }
39
+ }
40
+
41
+ let gInstance: DevToolsContext|null = null;
42
+
43
+ /**
44
+ * @deprecated Exists to migrate instance() methods.
45
+ */
46
+ export function globalInstance(): DevToolsContext {
47
+ if (!gInstance) {
48
+ // TODO(crbug.com/458180550): This should really throw to prevent side-effects and globals
49
+ // from leaking all over the place.
50
+ gInstance = new DevToolsContext();
51
+ }
52
+ return gInstance;
53
+ }
54
+
55
+ /**
56
+ * @deprecated Should only be called by test setup and MainImpl
57
+ */
58
+ export function setGlobalInstance(context: DevToolsContext|null): void {
59
+ gInstance = context;
60
+ }
@@ -1,6 +1,11 @@
1
1
  // Copyright 2020 The Chromium Authors
2
2
  // Use of this source code is governed by a BSD-style license that can be
3
3
  // found in the LICENSE file.
4
+
5
+ import * as DevToolsContext from './DevToolsContext.js';
4
6
  import * as Runtime from './Runtime.js';
5
7
 
6
- export {Runtime};
8
+ export {
9
+ DevToolsContext,
10
+ Runtime,
11
+ };
@@ -13,7 +13,6 @@ import * as Root from '../root/root.js';
13
13
  import {SDKModel} from './SDKModel.js';
14
14
  import {Target, Type as TargetType} from './Target.js';
15
15
 
16
- let targetManagerInstance: TargetManager|undefined;
17
16
  type ModelClass<T = SDKModel> = new (arg1: Target) => T;
18
17
 
19
18
  export class TargetManager extends Common.ObjectWrapper.ObjectWrapper<EventTypes> {
@@ -35,7 +34,7 @@ export class TargetManager extends Common.ObjectWrapper.ObjectWrapper<EventTypes
35
34
  #defaultScopeSet: boolean;
36
35
  readonly #scopeChangeListeners: Set<() => void>;
37
36
 
38
- private constructor() {
37
+ constructor() {
39
38
  super();
40
39
  this.#targets = new Set();
41
40
  this.#observers = new Set();
@@ -52,15 +51,15 @@ export class TargetManager extends Common.ObjectWrapper.ObjectWrapper<EventTypes
52
51
  static instance({forceNew}: {
53
52
  forceNew: boolean,
54
53
  } = {forceNew: false}): TargetManager {
55
- if (!targetManagerInstance || forceNew) {
56
- targetManagerInstance = new TargetManager();
54
+ if (!Root.DevToolsContext.globalInstance().has(TargetManager) || forceNew) {
55
+ Root.DevToolsContext.globalInstance().set(TargetManager, new TargetManager());
57
56
  }
58
57
 
59
- return targetManagerInstance;
58
+ return Root.DevToolsContext.globalInstance().get(TargetManager);
60
59
  }
61
60
 
62
61
  static removeInstance(): void {
63
- targetManagerInstance = undefined;
62
+ Root.DevToolsContext.globalInstance().delete(TargetManager);
64
63
  }
65
64
 
66
65
  onInspectedURLChange(target: Target): void {
@@ -46,20 +46,8 @@ const UIStrings = {
46
46
  } as const;
47
47
  const str_ = i18n.i18n.registerUIStrings('entrypoints/inspector_main/InspectorMain.ts', UIStrings);
48
48
  const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
49
- let inspectorMainImplInstance: InspectorMainImpl;
50
49
 
51
50
  export class InspectorMainImpl implements Common.Runnable.Runnable {
52
- static instance(opts: {
53
- forceNew: boolean|null,
54
- } = {forceNew: null}): InspectorMainImpl {
55
- const {forceNew} = opts;
56
- if (!inspectorMainImplInstance || forceNew) {
57
- inspectorMainImplInstance = new InspectorMainImpl();
58
- }
59
-
60
- return inspectorMainImplInstance;
61
- }
62
-
63
51
  async run(): Promise<void> {
64
52
  let firstCall = true;
65
53
  await SDK.Connections.initMainConnection(async () => {
@@ -162,7 +150,7 @@ export class InspectorMainImpl implements Common.Runnable.Runnable {
162
150
  }
163
151
  }
164
152
 
165
- Common.Runnable.registerEarlyInitializationRunnable(InspectorMainImpl.instance);
153
+ Common.Runnable.registerEarlyInitializationRunnable(() => new InspectorMainImpl());
166
154
 
167
155
  export class ReloadActionDelegate implements UI.ActionRegistration.ActionDelegate {
168
156
  handleAction(_context: UI.Context.Context, actionId: string): boolean {
@@ -175,7 +175,8 @@ export class MainImpl {
175
175
  runSettingsMigration: !Host.InspectorFrontendHost.isUnderTest(),
176
176
  },
177
177
  };
178
- new Foundation.Universe.Universe(creationOptions);
178
+ const universe = new Foundation.Universe.Universe(creationOptions);
179
+ Root.DevToolsContext.setGlobalInstance(universe.context);
179
180
 
180
181
  await this.requestAndRegisterLocaleData();
181
182
 
@@ -445,7 +446,6 @@ export class MainImpl {
445
446
  SDK.TargetManager.Events.SUSPEND_STATE_CHANGED, this.#onSuspendStateChanged.bind(this));
446
447
 
447
448
  Workspace.FileManager.FileManager.instance({forceNew: true});
448
- Workspace.Workspace.WorkspaceImpl.instance();
449
449
 
450
450
  Bindings.NetworkProject.NetworkProjectManager.instance();
451
451
  const resourceMapping = new Bindings.ResourceMapping.ResourceMapping(
@@ -458,14 +458,12 @@ export class MainImpl {
458
458
  resourceMapping,
459
459
  targetManager,
460
460
  });
461
- Workspace.IgnoreListManager.IgnoreListManager.instance({
462
- forceNew: true,
463
- });
464
461
  Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding.instance({
465
462
  forceNew: true,
466
463
  resourceMapping,
467
464
  targetManager,
468
465
  ignoreListManager: Workspace.IgnoreListManager.IgnoreListManager.instance(),
466
+ workspace: Workspace.Workspace.WorkspaceImpl.instance(),
469
467
  });
470
468
  targetManager.setScopeTarget(targetManager.primaryPageTarget());
471
469
  UI.Context.Context.instance().addFlavorChangeListener(SDK.Target.Target, ({data}) => {
@@ -3,6 +3,9 @@
3
3
  // found in the LICENSE file.
4
4
 
5
5
  import * as Common from '../core/common/common.js';
6
+ import * as Root from '../core/root/root.js';
7
+ import * as SDK from '../core/sdk/sdk.js';
8
+ import * as Workspace from '../models/workspace/workspace.js';
6
9
 
7
10
  export interface CreationOptions {
8
11
  // Settings things
@@ -10,12 +13,21 @@ export interface CreationOptions {
10
13
  }
11
14
 
12
15
  export class Universe {
16
+ readonly context = new Root.DevToolsContext.DevToolsContext();
17
+
13
18
  constructor(options: CreationOptions) {
14
19
  // TODO(crbug.com/458180550): Store instance on a "DevToolsContext" instead.
15
20
  // For now the global is fine as we don't anticipate the MCP server to change settings.
16
- Common.Settings.Settings.instance({
21
+ const settings = Common.Settings.Settings.instance({
17
22
  forceNew: true,
18
23
  ...options.settingsCreationOptions,
19
24
  });
25
+
26
+ const targetManager = new SDK.TargetManager.TargetManager();
27
+ this.context.set(SDK.TargetManager.TargetManager, targetManager);
28
+ this.context.set(Workspace.Workspace.WorkspaceImpl, new Workspace.Workspace.WorkspaceImpl());
29
+
30
+ const ignoreListManager = new Workspace.IgnoreListManager.IgnoreListManager(settings, targetManager);
31
+ this.context.set(Workspace.IgnoreListManager.IgnoreListManager, ignoreListManager);
20
32
  }
21
33
  }
@@ -28,12 +28,14 @@ export class DebuggerWorkspaceBinding implements SDK.TargetManager.SDKModelObser
28
28
  readonly #liveLocationPromises: Set<Promise<void|Location|StackTraceTopFrameLocation|null>>;
29
29
  readonly pluginManager: DebuggerLanguagePluginManager;
30
30
  readonly ignoreListManager: Workspace.IgnoreListManager.IgnoreListManager;
31
+ readonly workspace: Workspace.Workspace.WorkspaceImpl;
31
32
 
32
33
  private constructor(
33
34
  resourceMapping: ResourceMapping, targetManager: SDK.TargetManager.TargetManager,
34
- ignoreListManager: Workspace.IgnoreListManager.IgnoreListManager) {
35
+ ignoreListManager: Workspace.IgnoreListManager.IgnoreListManager, workspace: Workspace.Workspace.WorkspaceImpl) {
35
36
  this.resourceMapping = resourceMapping;
36
37
  this.ignoreListManager = ignoreListManager;
38
+ this.workspace = workspace;
37
39
 
38
40
  this.#debuggerModelToData = new Map();
39
41
  targetManager.addModelListener(
@@ -62,17 +64,19 @@ 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
+ 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;
67
71
  if (!debuggerWorkspaceBindingInstance || forceNew) {
68
- if (!resourceMapping || !targetManager || !ignoreListManager) {
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
78
  debuggerWorkspaceBindingInstance =
75
- new DebuggerWorkspaceBinding(resourceMapping, targetManager, ignoreListManager);
79
+ new DebuggerWorkspaceBinding(resourceMapping, targetManager, ignoreListManager, workspace);
76
80
  }
77
81
 
78
82
  return debuggerWorkspaceBindingInstance;
@@ -271,11 +275,10 @@ export class DebuggerWorkspaceBinding implements SDK.TargetManager.SDKModelObser
271
275
  waitForUISourceCodeAdded(url: Platform.DevToolsPath.UrlString, target: SDK.Target.Target):
272
276
  Promise<Workspace.UISourceCode.UISourceCode> {
273
277
  return new Promise(resolve => {
274
- const workspace = Workspace.Workspace.WorkspaceImpl.instance();
275
- const descriptor = workspace.addEventListener(Workspace.Workspace.Events.UISourceCodeAdded, event => {
278
+ const descriptor = this.workspace.addEventListener(Workspace.Workspace.Events.UISourceCodeAdded, event => {
276
279
  const uiSourceCode = event.data;
277
280
  if (uiSourceCode.url() === url && NetworkProject.targetForUISourceCode(uiSourceCode) === target) {
278
- workspace.removeEventListener(Workspace.Workspace.Events.UISourceCodeAdded, descriptor.listener);
281
+ this.workspace.removeEventListener(Workspace.Workspace.Events.UISourceCodeAdded, descriptor.listener);
279
282
  resolve(uiSourceCode);
280
283
  }
281
284
  });
@@ -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 {
@@ -1158,7 +1158,6 @@ export class AppManifestTreeElement extends ApplicationPanelTreeElement {
1158
1158
  self.onInvokeElement(this.listItemElement, this.onInvoke.bind(this));
1159
1159
  const emptyView = new UI.EmptyWidget.EmptyWidget(
1160
1160
  i18nString(UIStrings.noManifestDetected), i18nString(UIStrings.manifestDescription));
1161
- // TODO(crbug.com/1156978): Replace UI.ReportView.ReportView with ReportView.ts web component.
1162
1161
  const reportView = new UI.ReportView.ReportView(i18nString(UIStrings.appManifest));
1163
1162
  this.view = new AppManifestView(emptyView, reportView, new Common.Throttler.Throttler(1000));
1164
1163
  UI.ARIAUtils.setLabel(this.listItemElement, i18nString(UIStrings.onInvokeManifestAlert));
@@ -153,7 +153,6 @@ export class OpenedWindowDetailsView extends UI.Widget.VBox {
153
153
  this.isWindowClosed = isWindowClosed;
154
154
 
155
155
  this.contentElement.classList.add('frame-details-container');
156
- // TODO(crbug.com/1156978): Replace UI.ReportView.ReportView with ReportView.ts web component.
157
156
  this.reportView = new UI.ReportView.ReportView(this.buildTitle());
158
157
 
159
158
  this.reportView.show(this.contentElement);
@@ -221,7 +220,6 @@ export class WorkerDetailsView extends UI.Widget.VBox {
221
220
  this.targetInfo = targetInfo;
222
221
 
223
222
  this.contentElement.classList.add('frame-details-container');
224
- // TODO(crbug.com/1156978): Replace UI.ReportView.ReportView with ReportView.ts web component.
225
223
  this.reportView =
226
224
  new UI.ReportView.ReportView(this.targetInfo.title || this.targetInfo.url || i18nString(UIStrings.worker));
227
225
 
@@ -202,7 +202,6 @@ export class ServiceWorkersView extends UI.Widget.VBox implements
202
202
  });
203
203
  this.registerRequiredCSS(serviceWorkersViewStyles);
204
204
 
205
- // TODO(crbug.com/1156978): Replace UI.ReportView.ReportView with ReportView.ts web component.
206
205
  this.currentWorkersView = new UI.ReportView.ReportView(i18n.i18n.lockedString('Service workers'));
207
206
  this.currentWorkersView.setBodyScrollable(false);
208
207
  this.contentElement.classList.add('service-worker-list');
@@ -221,7 +220,6 @@ export class ServiceWorkersView extends UI.Widget.VBox implements
221
220
 
222
221
  const othersDiv = this.contentElement.createChild('div', 'service-workers-other-origin');
223
222
  othersDiv.setAttribute('jslog', `${VisualLogging.section('other-origin')}`);
224
- // TODO(crbug.com/1156978): Replace UI.ReportView.ReportView with ReportView.ts web component.
225
223
  const othersView = new UI.ReportView.ReportView();
226
224
  othersView.setHeaderVisible(false);
227
225
  othersView.show(othersDiv);
@@ -181,7 +181,6 @@ export class StorageView extends UI.Widget.VBox {
181
181
  [Protocol.Storage.StorageType.Service_workers, 'rgb(255, 167, 36)'], // orange
182
182
  ]);
183
183
 
184
- // TODO(crbug.com/1156978): Replace UI.ReportView.ReportView with ReportView.ts web component.
185
184
  this.reportView = new UI.ReportView.ReportView(i18nString(UIStrings.storageTitle));
186
185
  this.reportView.registerRequiredCSS(storageViewStyles);
187
186