chrome-devtools-frontend 1.0.1697595 → 1.0.1698733

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 (124) hide show
  1. package/.agents/skills/version-control/SKILL.md +21 -9
  2. package/.stylelintignore +2 -0
  3. package/front_end/core/common/VersionController.ts +5 -1
  4. package/front_end/core/platform/HostRuntime.ts +12 -0
  5. package/front_end/core/platform/api/HostRuntime.ts +13 -0
  6. package/front_end/core/platform/browser/HostRuntime.ts +34 -0
  7. package/front_end/core/platform/node/HostRuntime.ts +40 -0
  8. package/front_end/core/sdk/AnimationModel.ts +21 -99
  9. package/front_end/core/sdk/CSSModel.ts +4 -2
  10. package/front_end/core/sdk/CSSPropertyParser.ts +4 -4
  11. package/front_end/core/sdk/CSSPropertyParserMatchers.ts +4 -44
  12. package/front_end/core/sdk/Connections.ts +6 -1
  13. package/front_end/core/sdk/ConsoleModel.ts +1 -12
  14. package/front_end/core/sdk/DOMModel.ts +3 -51
  15. package/front_end/core/sdk/DebuggerModel.ts +9 -8
  16. package/front_end/core/sdk/NetworkRequest.ts +1 -1
  17. package/front_end/core/sdk/PageFunctions.ts +187 -0
  18. package/front_end/core/sdk/Resource.ts +1 -1
  19. package/front_end/core/sdk/RuntimeModel.ts +1 -20
  20. package/front_end/core/sdk/Script.ts +21 -0
  21. package/front_end/core/sdk/SourceMap.ts +15 -2
  22. package/front_end/core/sdk/SourceMapCache.ts +13 -10
  23. package/front_end/core/sdk/SourceMapManager.ts +11 -9
  24. package/front_end/core/sdk/SourceMapScopeChainEntry.ts +59 -17
  25. package/front_end/core/sdk/SourceMapScopesInfo.ts +8 -2
  26. package/front_end/entrypoints/heap_snapshot_worker/ContextAnalyzer.ts +635 -0
  27. package/front_end/entrypoints/heap_snapshot_worker/HeapSnapshot.ts +26 -12
  28. package/front_end/entrypoints/heap_snapshot_worker/HeapSnapshotLoader.ts +16 -2
  29. package/front_end/entrypoints/heap_snapshot_worker/HeapSnapshotWorkerDispatcher.ts +4 -4
  30. package/front_end/entrypoints/heap_snapshot_worker/heap_snapshot_worker.ts +2 -0
  31. package/front_end/entrypoints/shell/shell.ts +1 -1
  32. package/front_end/generated/InspectorBackendCommands.ts +1 -1
  33. package/front_end/generated/protocol.ts +8 -2
  34. package/front_end/models/ai_assistance/DOMHelpers.ts +95 -0
  35. package/front_end/models/ai_assistance/EvaluateAction.ts +5 -42
  36. package/front_end/models/ai_assistance/ExtensionScope.ts +2 -31
  37. package/front_end/models/ai_assistance/agents/AiAgent.ts +2 -1
  38. package/front_end/models/ai_assistance/agents/ContextSelectionAgent.ts +27 -2
  39. package/front_end/models/ai_assistance/ai_assistance.ts +6 -4
  40. package/front_end/models/ai_assistance/contexts/DOMNodeContext.ts +10 -7
  41. package/front_end/models/ai_assistance/contexts/FileContext.ts +2 -10
  42. package/front_end/models/ai_assistance/skills/performance.md +4 -3
  43. package/front_end/models/ai_assistance/tools/GetSourceContent.ts +3 -2
  44. package/front_end/models/ai_assistance/tools/{GetFunctionCode.ts → GetTraceFunctionCode.ts} +26 -13
  45. package/front_end/models/ai_assistance/tools/{GetResourceContent.ts → GetTraceResourceContent.ts} +31 -11
  46. package/front_end/models/ai_assistance/tools/ListSources.ts +21 -14
  47. package/front_end/models/ai_assistance/tools/Tool.ts +2 -2
  48. package/front_end/models/ai_assistance/tools/ToolRegistry.ts +6 -6
  49. package/front_end/models/bindings/CompilerScriptMapping.ts +3 -10
  50. package/front_end/models/bindings/DebuggerLanguagePlugins.ts +48 -17
  51. package/front_end/models/bindings/ResourceScriptMapping.ts +2 -2
  52. package/front_end/models/bindings/StylesSourceMapping.ts +1 -1
  53. package/front_end/models/comment_manager/CD4ABridge.ts +55 -12
  54. package/front_end/models/comment_manager/CommentManager.ts +46 -91
  55. package/front_end/models/comment_manager/CommentThread.ts +145 -0
  56. package/front_end/models/comment_manager/comment_manager.ts +2 -0
  57. package/front_end/models/elements/DOMChanges.ts +238 -0
  58. package/front_end/models/elements/elements.ts +2 -0
  59. package/front_end/models/emulation/DeviceModeModel.ts +53 -0
  60. package/front_end/models/extensions/ExtensionAPI.ts +8 -6
  61. package/front_end/models/extensions/ExtensionEndpoint.ts +11 -8
  62. package/front_end/models/extensions/HostUrlPattern.ts +4 -3
  63. package/front_end/models/extensions/LanguageExtensionEndpoint.ts +3 -2
  64. package/front_end/models/extensions/RecorderExtensionEndpoint.ts +2 -1
  65. package/front_end/models/formatter/FormatterWorkerPool.ts +4 -6
  66. package/front_end/models/heap_snapshot/HeapSnapshotModel.ts +55 -0
  67. package/front_end/models/heap_snapshot/HeapSnapshotProxy.ts +18 -14
  68. package/front_end/models/issues_manager/IssuesManager.ts +5 -0
  69. package/front_end/models/issues_manager/WebInstallIssue.ts +63 -0
  70. package/front_end/models/issues_manager/descriptions/webInstallManifestMissingId.md +3 -0
  71. package/front_end/models/issues_manager/descriptions/webInstallManifestMissingNameOrShortName.md +3 -0
  72. package/front_end/models/issues_manager/descriptions/webInstallManifestParsingOrNetworkError.md +3 -0
  73. package/front_end/models/issues_manager/descriptions/webInstallNoManifest.md +3 -0
  74. package/front_end/models/issues_manager/descriptions/webInstallStartUrlInvalid.md +3 -0
  75. package/front_end/models/issues_manager/issues_manager.ts +2 -0
  76. package/front_end/models/workspace/UISourceCode.ts +20 -0
  77. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +7 -7
  78. package/front_end/panels/ai_assistance/components/AIv2MarkdownRenderer.ts +4 -10
  79. package/front_end/panels/ai_assistance/components/MarkdownRendererWithCodeBlock.ts +25 -5
  80. package/front_end/panels/browser_debugger/ObjectEventListenersSidebarPane.ts +2 -3
  81. package/front_end/panels/common/CommentThreadWidget.ts +221 -0
  82. package/front_end/{ui/comments → panels/common}/CommentsOverlayWidget.ts +117 -28
  83. package/front_end/{ui/comments → panels/common}/CommentsStatusBarPill.ts +5 -5
  84. package/front_end/panels/common/ExtensionServer.ts +2 -1
  85. package/front_end/panels/common/commentThreadWidget.css +173 -0
  86. package/front_end/{ui/comments → panels/common}/commentsOverlay.css +8 -1
  87. package/front_end/{ui/comments/comments-meta.ts → panels/common/common-meta.ts} +11 -11
  88. package/front_end/panels/common/common.ts +3 -0
  89. package/front_end/panels/elements/AdoptedStyleSheetTreeElement.ts +3 -3
  90. package/front_end/panels/elements/ElementsPanel.ts +5 -1
  91. package/front_end/panels/elements/ElementsTreeElement.ts +102 -21
  92. package/front_end/panels/elements/ElementsTreeOutline.ts +326 -91
  93. package/front_end/panels/elements/EventListenersWidget.ts +0 -1
  94. package/front_end/panels/elements/PropertiesWidget.ts +11 -10
  95. package/front_end/panels/elements/PropertyRenderer.ts +29 -25
  96. package/front_end/panels/elements/StylePropertyTreeElement.ts +6 -6
  97. package/front_end/panels/elements/elementsTreeOutline.css +7 -1
  98. package/front_end/panels/emulation/DeviceModeToolbar.ts +2 -3
  99. package/front_end/panels/event_listeners/EventListenersView.ts +235 -302
  100. package/front_end/panels/linear_memory_inspector/LinearMemoryInspectorController.ts +6 -7
  101. package/front_end/panels/linear_memory_inspector/components/linearMemoryInspector.css +5 -1
  102. package/front_end/panels/linear_memory_inspector/linear_memory_inspector-meta.ts +1 -1
  103. package/front_end/panels/mobile_throttling/ThrottlingSettingsTab.ts +10 -2
  104. package/front_end/panels/network/NetworkConfigView.ts +141 -116
  105. package/front_end/panels/network/NetworkPanel.ts +7 -4
  106. package/front_end/panels/network/RequestHeadersView.ts +1 -1
  107. package/front_end/panels/network/forward/BackendLinking.ts +5 -2
  108. package/front_end/panels/recorder/stepView.css +1 -0
  109. package/front_end/panels/security/SecurityPanel.ts +5 -5
  110. package/front_end/panels/security/SecurityPanelSidebar.ts +2 -11
  111. package/front_end/panels/settings/BackendLinkingSettingsTab.ts +7 -2
  112. package/front_end/panels/sources/DebuggerPlugin.ts +1 -1
  113. package/front_end/panels/sources/WatchExpressionsSidebarPane.ts +11 -16
  114. package/front_end/panels/sources/sources-meta.ts +1 -1
  115. package/front_end/panels/timeline/TimelineTreeView.ts +11 -1
  116. package/front_end/panels/timeline/overlays/OverlaysImpl.ts +0 -3
  117. package/front_end/third_party/chromium/README.chromium +1 -1
  118. package/front_end/ui/comments/CommentOverlayManager.ts +89 -42
  119. package/front_end/ui/comments/comments.ts +0 -4
  120. package/front_end/ui/legacy/Treeoutline.ts +4 -0
  121. package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +15 -8
  122. package/front_end/ui/legacy/components/perf_ui/FlameChart.ts +4 -0
  123. package/front_end/ui/visual_logging/KnownContextValues.ts +1 -0
  124. package/package.json +5 -5
@@ -404,6 +404,8 @@ declare global {
404
404
  chrome: PublicAPI.Chrome.DevTools.Chrome;
405
405
  webInspector?: APIImpl.InspectorExtensionAPI;
406
406
  }
407
+ var injectedExtensionAPI: Window['injectedExtensionAPI'];
408
+ var buildExtensionAPIInjectedScript: Window['buildExtensionAPIInjectedScript'];
407
409
  }
408
410
 
409
411
  export interface ExtensionDescriptor {
@@ -511,7 +513,7 @@ namespace APIImpl {
511
513
  }
512
514
  }
513
515
 
514
- self.injectedExtensionAPI = function(
516
+ globalThis.injectedExtensionAPI = function(
515
517
  extensionInfo: ExtensionDescriptor, inspectedTabId: string, themeName: string, keysToForward: number[],
516
518
  testHook: (extensionServer: APIImpl.ExtensionServerClient, extensionAPI: APIImpl.InspectorExtensionAPI) => unknown,
517
519
  injectedScriptId: number, targetWindowForTest?: Window): void {
@@ -1763,22 +1765,22 @@ self.injectedExtensionAPI = function(
1763
1765
  testHook(extensionServer, coreAPI);
1764
1766
  };
1765
1767
 
1766
- self.buildExtensionAPIInjectedScript = function(
1768
+ globalThis.buildExtensionAPIInjectedScript = function(
1767
1769
  extensionInfo: {
1768
1770
  startPage: string,
1769
1771
  name: string,
1770
1772
  exposeExperimentalAPIs: boolean,
1771
1773
  },
1772
1774
  inspectedTabId: string, themeName: string, keysToForward: number[],
1773
- testHook:
1774
- ((extensionServer: APIImpl.ExtensionServerClient, extensionAPI: APIImpl.InspectorExtensionAPI) => unknown)|
1775
- undefined): string {
1775
+ testHook: ((extensionServer: APIImpl.ExtensionServerClient, extensionAPI: APIImpl.InspectorExtensionAPI) =>
1776
+ unknown)|undefined): string {
1776
1777
  const argumentsJSON =
1777
1778
  [extensionInfo, inspectedTabId || null, themeName, keysToForward].map(_ => JSON.stringify(_)).join(',');
1778
1779
  if (!testHook) {
1779
1780
  testHook = (): void => {};
1780
1781
  }
1781
1782
  return '(function(injectedScriptId){ ' +
1782
- '(' + self.injectedExtensionAPI.toString() + ')(' + argumentsJSON + ',' + testHook + ', injectedScriptId);' +
1783
+ '(' + globalThis.injectedExtensionAPI.toString() + ')(' + argumentsJSON + ',' + testHook +
1784
+ ', injectedScriptId);' +
1783
1785
  '})';
1784
1786
  };
@@ -2,29 +2,31 @@
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
4
 
5
+ import type * as Platform from '../../core/platform/platform.js';
6
+
5
7
  interface Response {
6
8
  requestId: number;
7
9
  result: unknown;
8
10
  error: Error|null;
9
11
  }
10
12
 
11
- interface Event {
13
+ interface EventMessage {
12
14
  event: string;
13
15
  }
14
16
 
15
- type Message = MessageEvent<Response|Event>;
16
-
17
17
  export class ExtensionEndpoint {
18
- private readonly port: MessagePort;
18
+ private readonly port: Platform.HostRuntime.WorkerMessagePort;
19
19
  private nextRequestId = 0;
20
20
  private pendingRequests: Map<number, {
21
21
  resolve: (arg: unknown) => void,
22
22
  reject: (error: Error) => void,
23
23
  }>;
24
24
 
25
- constructor(port: MessagePort) {
25
+ constructor(port: Platform.HostRuntime.WorkerMessagePort) {
26
26
  this.port = port;
27
- this.port.onmessage = this.onResponse.bind(this);
27
+ this.port.addEventListener('message', (event: unknown) => this.onResponse(event as MessageEvent));
28
+ (this.port as {start?: () => void}).start?.();
29
+ (this.port as {unref?: () => void}).unref?.();
28
30
  this.pendingRequests = new Map();
29
31
  }
30
32
 
@@ -44,7 +46,8 @@ export class ExtensionEndpoint {
44
46
  this.port.close();
45
47
  }
46
48
 
47
- private onResponse({data}: Message): void {
49
+ private onResponse(event: MessageEvent): void {
50
+ const data = event.data as Response | EventMessage;
48
51
  if ('event' in data) {
49
52
  this.handleEvent(data);
50
53
  return;
@@ -63,7 +66,7 @@ export class ExtensionEndpoint {
63
66
  }
64
67
  }
65
68
 
66
- protected handleEvent(_event: Event): void {
69
+ protected handleEvent(_event: EventMessage): void {
67
70
  throw new Error('handleEvent is not implemented');
68
71
  }
69
72
  }
@@ -77,8 +77,9 @@ function parseHostAndPort(pattern: string, scheme: string): {host: string, port:
77
77
  asUrl.hostname = asUrl.hostname.substr(0, asUrl.hostname.length - 1);
78
78
  }
79
79
 
80
- // The URL constructor is happy to accept '*', but it gets replaced with %2A
81
- if (asUrl.hostname !== '%2A' && asUrl.hostname.includes('%2A')) {
80
+ // The URL constructor in Chrome replaces '*' with '%2A', whereas Node preserves '*'.
81
+ const isWildcardHost = asUrl.hostname === '%2A' || asUrl.hostname === '*';
82
+ if (!isWildcardHost && (asUrl.hostname.includes('%2A') || asUrl.hostname.includes('*'))) {
82
83
  return undefined;
83
84
  }
84
85
 
@@ -93,7 +94,7 @@ function parseHostAndPort(pattern: string, scheme: string): {host: string, port:
93
94
  return undefined;
94
95
  }
95
96
 
96
- const host = asUrl.hostname !== '%2A' ? (pattern.startsWith('*.') ? `*.${asUrl.hostname}` : asUrl.hostname) : '*';
97
+ const host = !isWildcardHost ? (pattern.startsWith('*.') ? `*.${asUrl.hostname}` : asUrl.hostname) : '*';
97
98
  return {
98
99
  host,
99
100
  port,
@@ -13,7 +13,7 @@ import {ExtensionEndpoint} from './ExtensionEndpoint.js';
13
13
  class LanguageExtensionEndpointImpl extends ExtensionEndpoint {
14
14
  private plugin: LanguageExtensionEndpoint;
15
15
  #pluginManager: Bindings.DebuggerLanguagePlugins.DebuggerLanguagePluginManager;
16
- constructor(plugin: LanguageExtensionEndpoint, port: MessagePort,
16
+ constructor(plugin: LanguageExtensionEndpoint, port: Platform.HostRuntime.WorkerMessagePort,
17
17
  pluginManager: Bindings.DebuggerLanguagePlugins.DebuggerLanguagePluginManager) {
18
18
  super(port);
19
19
  this.plugin = plugin;
@@ -46,7 +46,8 @@ export class LanguageExtensionEndpoint implements Bindings.DebuggerLanguagePlugi
46
46
  // eslint-disable-next-line @typescript-eslint/naming-convention
47
47
  symbol_types: string[],
48
48
  },
49
- port: MessagePort, pluginManager: Bindings.DebuggerLanguagePlugins.DebuggerLanguagePluginManager) {
49
+ port: Platform.HostRuntime.WorkerMessagePort,
50
+ pluginManager: Bindings.DebuggerLanguagePlugins.DebuggerLanguagePluginManager) {
50
51
  this.name = name;
51
52
  this.extensionOrigin = extensionOrigin;
52
53
  this.supportedScriptTypes = supportedScriptTypes;
@@ -15,7 +15,8 @@ export class RecorderExtensionEndpoint extends ExtensionEndpoint {
15
15
  readonly #extensionOrigin: Platform.DevToolsPath.UrlString;
16
16
  readonly #recorderPluginManager: RecorderPluginManager;
17
17
 
18
- constructor(name: string, port: MessagePort, capabilities: PrivateAPI.RecordingExtensionPluginCapability[],
18
+ constructor(name: string, port: Platform.HostRuntime.WorkerMessagePort,
19
+ capabilities: PrivateAPI.RecordingExtensionPluginCapability[],
19
20
  extensionOrigin: Platform.DevToolsPath.UrlString, recorderPluginManager: RecorderPluginManager,
20
21
  mediaType?: string) {
21
22
  super(port);
@@ -2,7 +2,6 @@
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
4
 
5
- import type * as PlatformApi from '../../core/platform/api/api.js';
6
5
  import * as Platform from '../../core/platform/platform.js';
7
6
  import * as FormatterActions from '../../entrypoints/formatter_actions/formatter_actions.js';
8
7
 
@@ -12,7 +11,7 @@ let formatterWorkerPoolInstance: FormatterWorkerPool|undefined;
12
11
 
13
12
  export class FormatterWorkerPool {
14
13
  private taskQueue: Task[];
15
- private workerTasks: Map<PlatformApi.HostRuntime.Worker, Task|null>;
14
+ private workerTasks: Map<Platform.HostRuntime.Worker, Task|null>;
16
15
  private entrypointURL: string;
17
16
 
18
17
  constructor(entrypointURL?: string) {
@@ -46,7 +45,7 @@ export class FormatterWorkerPool {
46
45
  formatterWorkerPoolInstance = undefined;
47
46
  }
48
47
 
49
- private createWorker(): PlatformApi.HostRuntime.Worker {
48
+ private createWorker(): Platform.HostRuntime.Worker {
50
49
  const worker = Platform.HostRuntime.HOST_RUNTIME.createWorker(this.entrypointURL);
51
50
  worker.onmessage = this.onWorkerMessage.bind(this, worker);
52
51
  worker.onerror = this.onWorkerError.bind(this, worker);
@@ -75,8 +74,7 @@ export class FormatterWorkerPool {
75
74
  }
76
75
  }
77
76
 
78
- private onWorkerMessage(worker: PlatformApi.HostRuntime.Worker, event: PlatformApi.HostRuntime.WorkerMessageEvent):
79
- void {
77
+ private onWorkerMessage(worker: Platform.HostRuntime.Worker, event: Platform.HostRuntime.WorkerMessageEvent): void {
80
78
  const task = this.workerTasks.get(worker);
81
79
  if (!task) {
82
80
  return;
@@ -91,7 +89,7 @@ export class FormatterWorkerPool {
91
89
  task.callback(event.data ? event.data : null);
92
90
  }
93
91
 
94
- private onWorkerError(worker: PlatformApi.HostRuntime.Worker, event: Event): void {
92
+ private onWorkerError(worker: Platform.HostRuntime.Worker, event: Event): void {
95
93
  console.error(event);
96
94
  const task = this.workerTasks.get(worker);
97
95
  worker.terminate();
@@ -363,6 +363,61 @@ export interface ObjectInfo {
363
363
  retainerCount: number;
364
364
  }
365
365
 
366
+ export interface ContextAnalysisResult {
367
+ /** Scopes with dead fields, sorted by their highest-ranked context. */
368
+ scopes: ScopeAnalysis[];
369
+ /** Scripts with live contexts that could not be analyzed because they lack embedded scope metadata. */
370
+ scriptsWithoutScopes: ScriptWithoutScopes[];
371
+ }
372
+
373
+ export interface ScopeAnalysis {
374
+ scopeInfoNodeIndex: number;
375
+ scopeInfoNodeId: number;
376
+ scriptNodeIndex: number;
377
+ scriptNodeId: number;
378
+ scriptName: string;
379
+ /** Source name of the scope, when available. */
380
+ scopeName?: string;
381
+ scopeStart: number;
382
+ scopeEnd: number;
383
+ /** Number of context-typed fields in each context belonging to this scope. */
384
+ contextFieldCount: number;
385
+ /** Contexts with dead fields, sorted descending by deadFieldsRetainedSizeSum. */
386
+ contexts: ContextAnalysis[];
387
+ }
388
+
389
+ export interface ContextAnalysis {
390
+ contextNodeIndex: number;
391
+ contextNodeId: number;
392
+ retainedSize: number;
393
+ /**
394
+ * The sum of the retained sizes of values in fields classified as dead.
395
+ * This is a ranking heuristic, not the number of bytes that would be
396
+ * reclaimed by clearing the fields.
397
+ */
398
+ deadFieldsRetainedSizeSum: number;
399
+ /** Fields classified as dead in this context, sorted descending by retainedSize. */
400
+ deadFields: ContextField[];
401
+ }
402
+
403
+ export interface ContextField {
404
+ name: string;
405
+ valueNodeIndex: number;
406
+ valueNodeId: number;
407
+ valueName: string;
408
+ valueType: string;
409
+ selfSize: number;
410
+ retainedSize: number;
411
+ }
412
+
413
+ export interface ScriptWithoutScopes {
414
+ scriptNodeIndex: number;
415
+ scriptNodeId: number;
416
+ scriptName: string;
417
+ /** Number of live context instances belonging to this script. */
418
+ contextCount: number;
419
+ }
420
+
366
421
  export interface HeapQueryOptions {
367
422
  className?: string;
368
423
  propertyName?: string;
@@ -3,7 +3,6 @@
3
3
  // found in the LICENSE file.
4
4
 
5
5
  import * as Common from '../../core/common/common.js';
6
- import type * as PlatformApi from '../../core/platform/api/api.js';
7
6
  import * as Platform from '../../core/platform/platform.js';
8
7
 
9
8
  import type {ChildrenProvider} from './ChildrenProvider.js';
@@ -17,7 +16,7 @@ export class HeapSnapshotWorkerProxy extends Common.ObjectWrapper.ObjectWrapper<
17
16
  callbacks: Map<number, (error?: string, result?: unknown) => void> =
18
17
  new Map<number, (error?: string, result?: unknown) => void>();
19
18
  readonly previousCallbacks: Set<number> = new Set<number>();
20
- readonly worker: PlatformApi.HostRuntime.Worker;
19
+ readonly worker: Platform.HostRuntime.Worker;
21
20
  interval?: ReturnType<typeof setInterval>;
22
21
  readonly workerUrl?: string;
23
22
 
@@ -69,17 +68,18 @@ export class HeapSnapshotWorkerProxy extends Common.ObjectWrapper.ObjectWrapper<
69
68
  this.postMessage({callId, disposition: 'evaluateForTest', source: script});
70
69
  }
71
70
 
72
- callFactoryMethod<T extends Object>(
73
- callback: null, objectId: string, methodName: string, proxyConstructor: new(...arg1: any[]) => T,
74
- transfer: PlatformApi.HostRuntime.WorkerTransferable[], ...methodArguments: any[]): T;
71
+ callFactoryMethod<T extends Object>(callback: null, objectId: string, methodName: string,
72
+ proxyConstructor: new(...arg1: any[]) => T,
73
+ transfer: Platform.HostRuntime.WorkerTransferable[],
74
+ ...methodArguments: any[]): T;
75
75
  callFactoryMethod<T extends Object>(callback: ((error?: string, result?: T) => void), objectId: string,
76
76
  methodName: string, proxyConstructor: new(...arg1: any[]) => T,
77
- transfer: PlatformApi.HostRuntime.WorkerTransferable[],
77
+ transfer: Platform.HostRuntime.WorkerTransferable[],
78
78
  ...methodArguments: any[]): null;
79
79
  callFactoryMethod<T extends Object>(callback: ((error?: string, result?: T) => void)|null, objectId: string,
80
80
  methodName: string, proxyConstructor: new(...arg1: any[]) => T,
81
- transfer: PlatformApi.HostRuntime.WorkerTransferable[],
82
- ...methodArguments: any[]): T|null {
81
+ transfer: Platform.HostRuntime.WorkerTransferable[], ...methodArguments: any[]): T
82
+ |null {
83
83
  const callId = this.nextCallId++;
84
84
  const newObjectId = this.nextObjectId++;
85
85
 
@@ -154,7 +154,7 @@ export class HeapSnapshotWorkerProxy extends Common.ObjectWrapper.ObjectWrapper<
154
154
  }
155
155
  }
156
156
 
157
- setupForSecondaryInit(port: PlatformApi.HostRuntime.WorkerMessagePort): Promise<void> {
157
+ setupForSecondaryInit(port: Platform.HostRuntime.WorkerMessagePort): Promise<void> {
158
158
  const callId = this.nextCallId++;
159
159
  const done = new Promise<void>((resolve, reject) => {
160
160
  this.callbacks.set(callId, error => {
@@ -175,7 +175,7 @@ export class HeapSnapshotWorkerProxy extends Common.ObjectWrapper.ObjectWrapper<
175
175
  return done;
176
176
  }
177
177
 
178
- messageReceived(event: PlatformApi.HostRuntime.WorkerMessageEvent): void {
178
+ messageReceived(event: Platform.HostRuntime.WorkerMessageEvent): void {
179
179
  const data = event.data;
180
180
  if (data.eventName) {
181
181
  if (this.eventHandler) {
@@ -199,7 +199,7 @@ export class HeapSnapshotWorkerProxy extends Common.ObjectWrapper.ObjectWrapper<
199
199
  callback(undefined, data.result);
200
200
  }
201
201
 
202
- postMessage(message: unknown, transfer?: PlatformApi.HostRuntime.WorkerTransferable[]): void {
202
+ postMessage(message: unknown, transfer?: Platform.HostRuntime.WorkerTransferable[]): void {
203
203
  this.worker.postMessage(message, transfer);
204
204
  }
205
205
  }
@@ -231,9 +231,9 @@ export class HeapSnapshotProxyObject {
231
231
  return this.worker.callFactoryMethod(null, String(this.objectId), methodName, proxyConstructor, [], ...args);
232
232
  }
233
233
 
234
- callFactoryMethodPromise<T extends Object>(
235
- methodName: string, proxyConstructor: new(...arg1: any[]) => T,
236
- transfer: PlatformApi.HostRuntime.WorkerTransferable[], ...args: any[]): Promise<T> {
234
+ callFactoryMethodPromise<T extends Object>(methodName: string, proxyConstructor: new(...arg1: any[]) => T,
235
+ transfer: Platform.HostRuntime.WorkerTransferable[],
236
+ ...args: any[]): Promise<T> {
237
237
  return new Promise((resolve, reject) => this.worker.callFactoryMethod((error, result) => {
238
238
  if (error) {
239
239
  reject(new Error(error));
@@ -348,6 +348,10 @@ export class HeapSnapshotProxy extends HeapSnapshotProxyObject {
348
348
  return this.callMethodPromise('getObjectInfo', nodeIndex);
349
349
  }
350
350
 
351
+ analyzeContexts(): Promise<HeapSnapshotModel.ContextAnalysisResult> {
352
+ return this.callMethodPromise('analyzeContexts');
353
+ }
354
+
351
355
  createEdgesProvider(nodeIndex: number, options?: HeapSnapshotModel.HeapEdgesQueryOptions): HeapSnapshotProviderProxy {
352
356
  return this.callFactoryMethod('createEdgesProvider', HeapSnapshotProviderProxy, nodeIndex, options);
353
357
  }
@@ -38,6 +38,7 @@ import {SourceFrameIssuesManager} from './SourceFrameIssuesManager.js';
38
38
  import {SRIMessageSignatureIssue} from './SRIMessageSignatureIssue.js';
39
39
  import {StylesheetLoadingIssue} from './StylesheetLoadingIssue.js';
40
40
  import {UnencodedDigestIssue} from './UnencodedDigestIssue.js';
41
+ import {WebInstallIssue} from './WebInstallIssue.js';
41
42
 
42
43
  export {Events} from './IssuesManagerEvents.js';
43
44
 
@@ -149,6 +150,10 @@ const issueCodeHandlers =
149
150
  Protocol.Audits.InspectorIssueCode.ConnectionAllowlistIssue,
150
151
  ConnectionAllowlistIssue.fromInspectorIssue,
151
152
  ],
153
+ [
154
+ Protocol.Audits.InspectorIssueCode.WebInstallIssue,
155
+ WebInstallIssue.fromInspectorIssue,
156
+ ],
152
157
  [
153
158
  Protocol.Audits.InspectorIssueCode.PermissionElementIssue,
154
159
  PermissionElementIssue.fromInspectorIssue,
@@ -0,0 +1,63 @@
1
+ // Copyright 2026 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
+ import type * as SDK from '../../core/sdk/sdk.js';
6
+ import * as Protocol from '../../generated/protocol.js';
7
+
8
+ import {Issue, IssueCategory, IssueKind} from './Issue.js';
9
+ import type {MarkdownIssueDescription} from './MarkdownIssueDescription.js';
10
+
11
+ export class WebInstallIssue extends Issue<Protocol.Audits.WebInstallIssueDetails> {
12
+ constructor(issueDetails: Protocol.Audits.WebInstallIssueDetails, issuesModel: SDK.IssuesModel.IssuesModel|null) {
13
+ super(`${Protocol.Audits.InspectorIssueCode.WebInstallIssue}::${issueDetails.reason}`, issueDetails, issuesModel);
14
+ }
15
+
16
+ override requests(): Protocol.Audits.AffectedRequest[] {
17
+ const {manifestUrl, reason} = this.details();
18
+ if (reason === Protocol.Audits.WebInstallIssueReason.NoManifest || !manifestUrl) {
19
+ return [];
20
+ }
21
+ return [{url: manifestUrl}];
22
+ }
23
+
24
+ getCategory(): IssueCategory {
25
+ return IssueCategory.OTHER;
26
+ }
27
+
28
+ getDescription(): MarkdownIssueDescription|null {
29
+ switch (this.details().reason) {
30
+ case Protocol.Audits.WebInstallIssueReason.ManifestParsingOrNetworkError:
31
+ return {file: 'webInstallManifestParsingOrNetworkError.md', links: []};
32
+ case Protocol.Audits.WebInstallIssueReason.StartUrlInvalid:
33
+ return {file: 'webInstallStartUrlInvalid.md', links: []};
34
+ case Protocol.Audits.WebInstallIssueReason.ManifestMissingNameOrShortName:
35
+ return {file: 'webInstallManifestMissingNameOrShortName.md', links: []};
36
+ case Protocol.Audits.WebInstallIssueReason.ManifestMissingId:
37
+ return {file: 'webInstallManifestMissingId.md', links: []};
38
+ case Protocol.Audits.WebInstallIssueReason.NoManifest:
39
+ return {file: 'webInstallNoManifest.md', links: []};
40
+ default:
41
+ console.warn('Unknown WebInstallIssueReason:', this.details().reason);
42
+ return null;
43
+ }
44
+ }
45
+
46
+ getKind(): IssueKind {
47
+ return IssueKind.PAGE_ERROR;
48
+ }
49
+
50
+ primaryKey(): string {
51
+ return JSON.stringify(this.details());
52
+ }
53
+
54
+ static fromInspectorIssue(issuesModel: SDK.IssuesModel.IssuesModel|null,
55
+ inspectorIssue: Protocol.Audits.InspectorIssue): WebInstallIssue[] {
56
+ const details = inspectorIssue.details.webInstallIssueDetails;
57
+ if (!details) {
58
+ console.warn('Web install issue without details received.');
59
+ return [];
60
+ }
61
+ return [new WebInstallIssue(details, issuesModel)];
62
+ }
63
+ }
@@ -0,0 +1,3 @@
1
+ # Manifest does not contain an `id` field
2
+
3
+ Add an `id` to the manifest. Alternatively, for installs using an explicit manifest URL, provide the computed manifest ID as `manifestId` alongside the manifest URL.
@@ -0,0 +1,3 @@
1
+ # Manifest does not contain a 'name' or 'short_name' field
2
+
3
+ Add a `name` or `short_name` field to the manifest.
@@ -0,0 +1,3 @@
1
+ # Manifest could not be fetched, is empty, or could not be parsed
2
+
3
+ Check that the web app manifest can be fetched and that it contains valid, non-empty JSON.
@@ -0,0 +1,3 @@
1
+ # No manifest detected
2
+
3
+ Provide a web app manifest for this page.
@@ -0,0 +1,3 @@
1
+ # Manifest 'start_url' is not valid
2
+
3
+ Provide a valid `start_url` in the manifest.
@@ -36,6 +36,7 @@ import * as SourceFrameIssuesManager from './SourceFrameIssuesManager.js';
36
36
  import * as SRIMessageSignatureIssue from './SRIMessageSignatureIssue.js';
37
37
  import * as StylesheetLoadingIssue from './StylesheetLoadingIssue.js';
38
38
  import * as UnencodedDigestIssue from './UnencodedDigestIssue.js';
39
+ import * as WebInstallIssue from './WebInstallIssue.js';
39
40
 
40
41
  export {
41
42
  ClientHintIssue,
@@ -72,4 +73,5 @@ export {
72
73
  SRIMessageSignatureIssue,
73
74
  StylesheetLoadingIssue,
74
75
  UnencodedDigestIssue,
76
+ WebInstallIssue,
75
77
  };
@@ -7,6 +7,7 @@
7
7
  import * as Common from '../../core/common/common.js';
8
8
  import * as i18n from '../../core/i18n/i18n.js';
9
9
  import * as Platform from '../../core/platform/platform.js';
10
+ import * as SDK from '../../core/sdk/sdk.js';
10
11
  import * as TextUtils from '../../core/text_utils/text_utils.js';
11
12
 
12
13
  import {IgnoreListManager} from './IgnoreListManager.js';
@@ -29,6 +30,7 @@ export class UISourceCode extends Common.ObjectWrapper.ObjectWrapper<EventTypes>
29
30
  TextUtils.ContentProvider.ContentProvider {
30
31
  readonly #origin: Platform.DevToolsPath.UrlString;
31
32
  readonly #parentURL: Platform.DevToolsPath.UrlString;
33
+ #securityOrigin?: SDK.SecurityOrigin.SecurityOrigin;
32
34
  #project: Project;
33
35
  #url: Platform.DevToolsPath.UrlString;
34
36
  #name: string;
@@ -110,6 +112,23 @@ export class UISourceCode extends Common.ObjectWrapper.ObjectWrapper<EventTypes>
110
112
  return this.#origin;
111
113
  }
112
114
 
115
+ /**
116
+ * Returns the security origin for this source code.
117
+ * Prefers the project security origin if available. If the project does not
118
+ * define a security origin, derives it from the source code's URL and caches
119
+ * the result until the source code is renamed.
120
+ */
121
+ securityOrigin(): SDK.SecurityOrigin.SecurityOrigin {
122
+ const projectOrigin = this.#project.securityOrigin?.();
123
+ if (projectOrigin) {
124
+ return projectOrigin;
125
+ }
126
+ if (!this.#securityOrigin) {
127
+ this.#securityOrigin = SDK.SecurityOrigin.SecurityOrigin.create(this.#url);
128
+ }
129
+ return this.#securityOrigin;
130
+ }
131
+
113
132
  fullDisplayName(): string {
114
133
  return this.#project.fullDisplayName(this);
115
134
  }
@@ -150,6 +169,7 @@ export class UISourceCode extends Common.ObjectWrapper.ObjectWrapper<EventTypes>
150
169
  #updateName(
151
170
  name: Platform.DevToolsPath.RawPathString, url: Platform.DevToolsPath.UrlString,
152
171
  contentType?: Common.ResourceType.ResourceType): void {
172
+ this.#securityOrigin = undefined;
153
173
  const oldURL = this.#url;
154
174
  this.#name = name;
155
175
  if (url) {
@@ -282,12 +282,10 @@ async function getEmptyStateSuggestions(conversation?: AiAssistanceModel.AiConve
282
282
  }
283
283
  }
284
284
 
285
- function createV2MarkdownRenderer(conversation?: AiAssistanceModel.AiConversation.AiConversation):
286
- AIv2MarkdownRenderer {
287
- const options: AIv2MarkdownRendererOptions = {};
288
- if (conversation) {
289
- options.getEstablishedOrigin = () => conversation.origin;
290
- }
285
+ function createV2MarkdownRenderer(conversation: AiAssistanceModel.AiConversation.AiConversation): AIv2MarkdownRenderer {
286
+ const options: AIv2MarkdownRendererOptions = {
287
+ getOriginLock: () => conversation.getOriginLock(),
288
+ };
291
289
  const primaryTarget = SDK.TargetManager.TargetManager.instance().primaryPageTarget();
292
290
  const domModel = primaryTarget?.model(SDK.DOMModel.DOMModel);
293
291
  const resourceTreeModel = primaryTarget?.model(SDK.ResourceTreeModel.ResourceTreeModel);
@@ -337,7 +335,9 @@ function getMarkdownRenderer(conversation?: AiAssistanceModel.AiConversation.AiC
337
335
  return new AccessibilityAgentMarkdownRenderer(mainDocumentURL);
338
336
  }
339
337
 
340
- return new MarkdownRendererWithCodeBlock();
338
+ return new MarkdownRendererWithCodeBlock({
339
+ getEstablishedOrigin: () => conversation?.origin,
340
+ });
341
341
  }
342
342
 
343
343
  interface ToolbarViewInput {
@@ -21,13 +21,10 @@ export interface AIv2MarkdownRendererOptions {
21
21
  mainFrameId?: string;
22
22
  mainDocumentURL?: Platform.DevToolsPath.UrlString;
23
23
  /**
24
- * Retrieves the established origin locked for the active conversation.
24
+ * Retrieves the origin lock for the active conversation.
25
25
  * Required to authorize #file-<id> links and prevent cross-origin file leakage.
26
- *
27
- * TODO(crbug.com/559522248): Defer renderer instantiation until a conversation
28
- * starts so this origin can be passed as a mandatory, non-getter property.
29
26
  */
30
- getEstablishedOrigin?: () => SDK.SecurityOrigin.SecurityOrigin | undefined;
27
+ getOriginLock: () => AiAssistanceModel.Tool.OriginLockState;
31
28
  lookupTraceEvent?: (key: string) => Trace.Types.Events.Event | null;
32
29
  }
33
30
 
@@ -45,7 +42,7 @@ type ParsedLink = {
45
42
  * the only markdown renderer used by AI assistance.
46
43
  */
47
44
  export class AIv2MarkdownRenderer extends MarkdownView.MarkdownView.MarkdownInsightRenderer {
48
- constructor(private readonly options: AIv2MarkdownRendererOptions = {}) {
45
+ constructor(private readonly options: AIv2MarkdownRendererOptions) {
49
46
  super();
50
47
  }
51
48
 
@@ -119,10 +116,7 @@ export class AIv2MarkdownRenderer extends MarkdownView.MarkdownView.MarkdownInsi
119
116
  }
120
117
  if (href.startsWith('#file-')) {
121
118
  const fileId = Number(href.substring(6));
122
- const origin = this.options.getEstablishedOrigin?.();
123
- const file = (origin && Number.isInteger(fileId) && fileId > 0) ?
124
- AiAssistanceModel.ListSources.ListSourcesTool.getSourceById(fileId, origin) :
125
- undefined;
119
+ const file = AiAssistanceModel.ListSources.ListSourcesTool.getSourceById(fileId, this.options.getOriginLock());
126
120
 
127
121
  if (file) {
128
122
  return this.#revealableLink(file, file.name());
@@ -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 type * as SDK from '../../../core/sdk/sdk.js';
7
8
  import * as AiAssistanceModel from '../../../models/ai_assistance/ai_assistance.js';
8
9
  import * as Logs from '../../../models/logs/logs.js';
9
10
  import type * as Marked from '../../../third_party/marked/marked.js';
@@ -12,7 +13,18 @@ import * as Lit from '../../../ui/lit/lit.js';
12
13
 
13
14
  const {html} = Lit;
14
15
 
16
+ export interface MarkdownRendererWithCodeBlockOptions {
17
+ /**
18
+ * Retrieves the established origin locked for the active conversation.
19
+ * Required to authorize #file-<id> links and prevent cross-origin file leakage.
20
+ * When omitted or unauthorized, file links safely render as plain inert text.
21
+ */
22
+ getEstablishedOrigin?: () => SDK.SecurityOrigin.SecurityOrigin | undefined;
23
+ }
24
+
15
25
  /**
26
+ * Markdown renderer for AI assistance conversations.
27
+ *
16
28
  * The model returns multiline code blocks in an erroneous way with the language being in new line.
17
29
  * This renderer takes that into account and correctly updates the parsed multiline token with the language
18
30
  * correctly identified and stripped from the content.
@@ -23,8 +35,13 @@ const {html} = Lit;
23
35
  * color: red;
24
36
  * }
25
37
  * ```
26
- **/
38
+ *
39
+ * Also handles linkifying DevTools internal resource links (#req- and origin-locked #file-).
40
+ */
27
41
  export class MarkdownRendererWithCodeBlock extends MarkdownView.MarkdownView.MarkdownInsightRenderer {
42
+ constructor(private readonly options: MarkdownRendererWithCodeBlockOptions = {}) {
43
+ super();
44
+ }
28
45
  #revealableLink(revealable: unknown, label: string): Lit.LitTemplate {
29
46
  return html`<devtools-link @click=${(e: Event) => {
30
47
  e.preventDefault();
@@ -48,13 +65,16 @@ export class MarkdownRendererWithCodeBlock extends MarkdownView.MarkdownView.Mar
48
65
  return html`${fallbackText}`;
49
66
  }
50
67
  if (href.startsWith('#file-')) {
51
- const file = AiAssistanceModel.ContextSelectionAgent.ContextSelectionAgent.getUISourceCodes().find(
52
- file => AiAssistanceModel.ContextSelectionAgent.ContextSelectionAgent.uiSourceCodeId.get(file) ===
53
- Number(href.substring(6)));
68
+ const fileId = Number(href.substring(6));
69
+ const file = AiAssistanceModel.ContextSelectionAgent.ContextSelectionAgent.getSourceById(
70
+ fileId,
71
+ this.options.getEstablishedOrigin?.(),
72
+ );
54
73
 
55
74
  if (file) {
56
75
  return this.#revealableLink(file, file.name());
57
76
  }
77
+ // Fall back to plain text to prevent rendering unauthorized or unverified links.
58
78
  return html`${fallbackText}`;
59
79
  }
60
80
  return null;
@@ -78,7 +98,7 @@ export class MarkdownRendererWithCodeBlock extends MarkdownView.MarkdownView.Mar
78
98
 
79
99
  if (token.type === 'codespan') {
80
100
  // LLM likes outputting the link inside a codespan block.
81
- // Remove the codespan and render the link directly
101
+ // Remove the codespan and render the link directly.
82
102
  const matches = token.text.match(/^\[(.*)\]\((.+)\)$/);
83
103
  if (matches?.[2]) {
84
104
  const link = this.#renderLink(