chrome-devtools-frontend 1.0.1642845 → 1.0.1643099

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 (128) hide show
  1. package/SECURITY.md +1 -0
  2. package/front_end/core/host/UserMetrics.ts +2 -1
  3. package/front_end/core/protocol_client/InspectorBackend.ts +4 -0
  4. package/front_end/core/sdk/CSSMatchedStyles.ts +55 -26
  5. package/front_end/core/sdk/CSSRule.ts +1 -0
  6. package/front_end/core/sdk/DebuggerModel.ts +5 -0
  7. package/front_end/entrypoints/greendev_floaty/FloatyEntrypoint.ts +4 -3
  8. package/front_end/entrypoints/greendev_floaty/greendev_floaty.ts +4 -3
  9. package/front_end/entrypoints/heap_snapshot_worker/HeapSnapshot.ts +4 -5
  10. package/front_end/generated/InspectorBackendCommands.ts +1 -1
  11. package/front_end/generated/protocol.ts +7 -0
  12. package/front_end/models/ai_assistance/AiAgent2.ts +100 -18
  13. package/front_end/models/ai_assistance/AiConversation.ts +18 -14
  14. package/front_end/models/ai_assistance/AiUtils.ts +71 -0
  15. package/front_end/models/ai_assistance/ChangeManager.ts +2 -5
  16. package/front_end/models/ai_assistance/{agents/ConversationSummaryAgent.ts → ConversationSummary.ts} +29 -66
  17. package/front_end/models/ai_assistance/ExtensionScope.ts +1 -4
  18. package/front_end/models/ai_assistance/{agents/PerformanceAnnotationsAgent.ts → PerformanceAnnotations.ts} +47 -89
  19. package/front_end/models/ai_assistance/README.md +8 -0
  20. package/front_end/models/ai_assistance/agents/AccessibilityAgent.ts +65 -40
  21. package/front_end/models/ai_assistance/agents/AiAgent.ts +37 -6
  22. package/front_end/models/ai_assistance/agents/ContextSelectionAgent.snapshot.txt +11 -0
  23. package/front_end/models/ai_assistance/agents/ContextSelectionAgent.ts +55 -5
  24. package/front_end/models/ai_assistance/agents/ExecuteJavascript.ts +2 -0
  25. package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +119 -78
  26. package/front_end/models/ai_assistance/agents/StorageAgent.ts +47 -38
  27. package/front_end/models/ai_assistance/agents/StylingAgent.snapshot.txt +0 -25
  28. package/front_end/models/ai_assistance/agents/StylingAgent.ts +46 -326
  29. package/front_end/models/ai_assistance/ai_assistance.ts +14 -4
  30. package/front_end/models/ai_assistance/contexts/DOMNodeContext.snapshot.txt +51 -0
  31. package/front_end/models/ai_assistance/contexts/DOMNodeContext.ts +200 -0
  32. package/front_end/models/ai_assistance/skills/styling.md +44 -2
  33. package/front_end/models/ai_assistance/tools/ExecuteJavaScript.ts +140 -0
  34. package/front_end/models/ai_assistance/tools/GetStyles.ts +141 -0
  35. package/front_end/models/ai_assistance/tools/Tool.ts +64 -0
  36. package/front_end/models/ai_assistance/tools/ToolRegistry.ts +36 -0
  37. package/front_end/models/heap_snapshot/HeapSnapshotProxy.ts +5 -7
  38. package/front_end/models/lighthouse/LighthouseReporterTypes.ts +5 -0
  39. package/front_end/models/live-metrics/LiveMetrics.ts +24 -13
  40. package/front_end/models/stack_trace/DetailedErrorStackParser.ts +2 -2
  41. package/front_end/models/stack_trace/StackTrace.ts +4 -1
  42. package/front_end/models/stack_trace/StackTraceImpl.ts +9 -2
  43. package/front_end/models/stack_trace/StackTraceModel.ts +17 -4
  44. package/front_end/models/stack_trace/Trie.ts +1 -1
  45. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +25 -22
  46. package/front_end/panels/ai_assistance/ai_assistance-meta.ts +16 -0
  47. package/front_end/panels/ai_assistance/components/ChatInput.ts +2 -2
  48. package/front_end/panels/ai_assistance/components/ChatMessage.ts +96 -4
  49. package/front_end/panels/ai_assistance/components/chatMessage.css +6 -0
  50. package/front_end/panels/application/DOMStorageItemsView.ts +4 -0
  51. package/front_end/panels/application/KeyValueStorageItemsView.ts +39 -7
  52. package/front_end/panels/application/components/AdsView.ts +219 -0
  53. package/front_end/panels/application/components/adsView.css +54 -0
  54. package/front_end/panels/application/components/components.ts +2 -0
  55. package/front_end/panels/common/ExtensionServer.ts +26 -15
  56. package/front_end/panels/console/SymbolizedErrorWidget.ts +73 -22
  57. package/front_end/panels/elements/StandaloneStylesContainer.ts +1 -1
  58. package/front_end/panels/elements/StylePropertiesSection.ts +8 -0
  59. package/front_end/panels/elements/StylePropertyHighlighter.ts +4 -2
  60. package/front_end/panels/elements/StylePropertyTreeElement.ts +6 -5
  61. package/front_end/panels/elements/StylesContainer.ts +1 -1
  62. package/front_end/panels/elements/StylesSidebarPane.ts +4 -4
  63. package/front_end/panels/layer_viewer/PaintProfilerView.ts +106 -132
  64. package/front_end/panels/lighthouse/LighthousePanel.ts +4 -3
  65. package/front_end/panels/network/NetworkLogView.ts +8 -1
  66. package/front_end/panels/network/networkLogView.css +0 -15
  67. package/front_end/panels/timeline/overlays/components/EntryLabelOverlay.ts +5 -4
  68. package/front_end/third_party/chromium/README.chromium +1 -1
  69. package/front_end/third_party/lighthouse/README.chromium +2 -2
  70. package/front_end/third_party/lighthouse/lighthouse-dt-bundle.js +1607 -5733
  71. package/front_end/third_party/lighthouse/locales/ar-XB.json +290 -65
  72. package/front_end/third_party/lighthouse/locales/ar.json +290 -65
  73. package/front_end/third_party/lighthouse/locales/bg.json +290 -65
  74. package/front_end/third_party/lighthouse/locales/ca.json +295 -70
  75. package/front_end/third_party/lighthouse/locales/cs.json +290 -65
  76. package/front_end/third_party/lighthouse/locales/da.json +294 -69
  77. package/front_end/third_party/lighthouse/locales/de.json +295 -70
  78. package/front_end/third_party/lighthouse/locales/el.json +290 -65
  79. package/front_end/third_party/lighthouse/locales/en-GB.json +290 -65
  80. package/front_end/third_party/lighthouse/locales/en-US.json +79 -67
  81. package/front_end/third_party/lighthouse/locales/en-XA.json +253 -64
  82. package/front_end/third_party/lighthouse/locales/en-XL.json +79 -67
  83. package/front_end/third_party/lighthouse/locales/es-419.json +290 -65
  84. package/front_end/third_party/lighthouse/locales/es.json +298 -73
  85. package/front_end/third_party/lighthouse/locales/fi.json +290 -65
  86. package/front_end/third_party/lighthouse/locales/fil.json +290 -65
  87. package/front_end/third_party/lighthouse/locales/fr.json +294 -69
  88. package/front_end/third_party/lighthouse/locales/he.json +293 -68
  89. package/front_end/third_party/lighthouse/locales/hi.json +291 -66
  90. package/front_end/third_party/lighthouse/locales/hr.json +290 -65
  91. package/front_end/third_party/lighthouse/locales/hu.json +290 -65
  92. package/front_end/third_party/lighthouse/locales/id.json +290 -65
  93. package/front_end/third_party/lighthouse/locales/it.json +294 -69
  94. package/front_end/third_party/lighthouse/locales/ja.json +290 -65
  95. package/front_end/third_party/lighthouse/locales/ko.json +290 -65
  96. package/front_end/third_party/lighthouse/locales/lt.json +290 -65
  97. package/front_end/third_party/lighthouse/locales/lv.json +290 -65
  98. package/front_end/third_party/lighthouse/locales/nl.json +290 -65
  99. package/front_end/third_party/lighthouse/locales/no.json +290 -65
  100. package/front_end/third_party/lighthouse/locales/pl.json +290 -65
  101. package/front_end/third_party/lighthouse/locales/pt-PT.json +291 -66
  102. package/front_end/third_party/lighthouse/locales/pt.json +290 -65
  103. package/front_end/third_party/lighthouse/locales/ro.json +290 -65
  104. package/front_end/third_party/lighthouse/locales/ru.json +301 -76
  105. package/front_end/third_party/lighthouse/locales/sk.json +291 -66
  106. package/front_end/third_party/lighthouse/locales/sl.json +290 -65
  107. package/front_end/third_party/lighthouse/locales/sr-Latn.json +290 -65
  108. package/front_end/third_party/lighthouse/locales/sr.json +290 -65
  109. package/front_end/third_party/lighthouse/locales/sv.json +297 -72
  110. package/front_end/third_party/lighthouse/locales/ta.json +291 -66
  111. package/front_end/third_party/lighthouse/locales/te.json +293 -68
  112. package/front_end/third_party/lighthouse/locales/th.json +291 -66
  113. package/front_end/third_party/lighthouse/locales/tr.json +290 -65
  114. package/front_end/third_party/lighthouse/locales/uk.json +290 -65
  115. package/front_end/third_party/lighthouse/locales/vi.json +291 -66
  116. package/front_end/third_party/lighthouse/locales/zh-HK.json +292 -67
  117. package/front_end/third_party/lighthouse/locales/zh-TW.json +291 -66
  118. package/front_end/third_party/lighthouse/locales/zh.json +291 -66
  119. package/front_end/third_party/lighthouse/report/bundle.d.ts +6 -6
  120. package/front_end/third_party/lighthouse/report/bundle.js +4 -7
  121. package/front_end/third_party/lighthouse/report-assets/report-generator.mjs +2 -2
  122. package/front_end/ui/legacy/Widget.ts +32 -8
  123. package/front_end/ui/legacy/components/cookie_table/CookiesTable.ts +36 -3
  124. package/front_end/ui/legacy/components/data_grid/dataGridAiButton.css +20 -0
  125. package/front_end/ui/legacy/components/utils/Linkifier.ts +19 -4
  126. package/front_end/ui/visual_logging/KnownContextValues.ts +3 -0
  127. package/mcp/mcp.ts +1 -0
  128. package/package.json +1 -1
@@ -77,6 +77,11 @@ export interface ReportJSON {
77
77
  audits: Record<string, AuditResultJSON>;
78
78
  categories: Record<CategoryId, CategoryJSON>;
79
79
  categoryGroups: Record<string, GroupJSON>;
80
+ /**
81
+ * Identifies if the report was imported from a file (untrusted).
82
+ * Used to disable page-touching AI assistance tools for security.
83
+ */
84
+ isImported?: boolean;
80
85
  }
81
86
 
82
87
  export type DetailsJSON =
@@ -64,17 +64,22 @@ export class LiveMetrics extends Common.ObjectWrapper.ObjectWrapper<EventTypes>
64
64
  super();
65
65
  const targetManager = SDK.TargetManager.TargetManager.instance();
66
66
  targetManager.observeTargets(this, {scoped: true});
67
- // Listen for target info changes to detect prerender activation.
68
- // Scoped observers don't receive events when a prerendered target becomes
69
- // primary because setScopeTarget() isn't called during that transition.
70
- targetManager.addEventListener(
71
- SDK.TargetManager.Events.AVAILABLE_TARGETS_CHANGED, this.#onAvailableTargetsChanged, this);
67
+ targetManager.addModelListener(
68
+ SDK.ResourceTreeModel.ResourceTreeModel, SDK.ResourceTreeModel.Events.PrimaryPageChanged,
69
+ this.#onPrimaryPageChanged, this);
72
70
  }
73
71
 
74
- #onAvailableTargetsChanged(): void {
72
+ #onPrimaryPageChanged(
73
+ event: Common.EventTarget.EventTargetEvent<
74
+ {frame: SDK.ResourceTreeModel.ResourceTreeFrame, type: SDK.ResourceTreeModel.PrimaryPageChangeType}>): void {
75
75
  const primaryTarget = SDK.TargetManager.TargetManager.instance().primaryPageTarget();
76
- if (primaryTarget && primaryTarget !== this.#target) {
77
- // Primary target changed (e.g., prerender activation). Switch to it.
76
+ if (!primaryTarget) {
77
+ return;
78
+ }
79
+ if (primaryTarget !== this.#target || event.data.type === SDK.ResourceTreeModel.PrimaryPageChangeType.ACTIVATION) {
80
+ // Primary target changed or prerender activated. Switch to it and reset metrics.
81
+ this.#clearMetrics();
82
+ this.#sendStatusUpdate();
78
83
  void this.#switchToTarget(primaryTarget);
79
84
  }
80
85
  }
@@ -381,11 +386,7 @@ export class LiveMetrics extends Common.ObjectWrapper.ObjectWrapper<EventTypes>
381
386
  break;
382
387
  }
383
388
  case 'reset': {
384
- this.#lcpValue = undefined;
385
- this.#clsValue = undefined;
386
- this.#inpValue = undefined;
387
- this.#interactions.clear();
388
- this.#layoutShifts = [];
389
+ this.#clearMetrics();
389
390
  break;
390
391
  }
391
392
  }
@@ -393,6 +394,15 @@ export class LiveMetrics extends Common.ObjectWrapper.ObjectWrapper<EventTypes>
393
394
  this.#sendStatusUpdate();
394
395
  }
395
396
 
397
+ #clearMetrics(): void {
398
+ this.#lcpValue = undefined;
399
+ this.#clsValue = undefined;
400
+ this.#inpValue = undefined;
401
+ this.#interactions.clear();
402
+ this.#interactionsByGroupId.clear();
403
+ this.#layoutShifts = [];
404
+ }
405
+
396
406
  #isPrimaryFrameExecutionContext(executionContextId: Protocol.Runtime.ExecutionContextId): boolean {
397
407
  if (!this.#target) {
398
408
  return false;
@@ -460,6 +470,7 @@ export class LiveMetrics extends Common.ObjectWrapper.ObjectWrapper<EventTypes>
460
470
 
461
471
  clearInteractions(): void {
462
472
  this.#interactions.clear();
473
+ this.#interactionsByGroupId.clear();
463
474
  this.#sendStatusUpdate();
464
475
  }
465
476
 
@@ -142,12 +142,12 @@ export function parseRawFramesFromErrorStack(stack: string): RawFrame[]|null {
142
142
  functionName,
143
143
  lineNumber,
144
144
  columnNumber,
145
+ isWasm,
145
146
  parsedFrameInfo: {
146
147
  isAsync,
147
148
  isConstructor,
148
149
  isEval,
149
150
  evalOrigin,
150
- isWasm,
151
151
  wasmModuleName,
152
152
  wasmFunctionIndex,
153
153
  typeName,
@@ -181,7 +181,7 @@ export function parseMessage(stack: string): string {
181
181
  export function augmentRawFramesWithScriptIds(
182
182
  rawFrames: RawFrame[], protocolStackTrace: Protocol.Runtime.StackTrace): void {
183
183
  function augmentFrame(rawFrame: RawFrame): void {
184
- const isWasm = rawFrame.parsedFrameInfo?.isWasm;
184
+ const isWasm = rawFrame.isWasm;
185
185
  const protocolFrame = protocolStackTrace.callFrames.find(frame => {
186
186
  if (isWasm) {
187
187
  // The parser parses Wasm offsets into the `columnNumber` field. The `lineNumber` is always -1.
@@ -44,6 +44,10 @@ export interface Frame {
44
44
  * of the containing function.
45
45
  */
46
46
  readonly rawName?: string;
47
+ /**
48
+ * Whether the corresponding raw frame is JS or WASM.
49
+ */
50
+ readonly isWasm?: boolean;
47
51
  }
48
52
 
49
53
  export interface ParsedErrorStackFrame extends Frame {
@@ -51,7 +55,6 @@ export interface ParsedErrorStackFrame extends Frame {
51
55
  readonly isConstructor?: boolean;
52
56
  readonly isEval?: boolean;
53
57
  readonly evalOrigin?: ParsedErrorStackFrame;
54
- readonly isWasm?: boolean;
55
58
  readonly wasmModuleName?: string;
56
59
  readonly wasmFunctionIndex?: number;
57
60
  readonly typeName?: string;
@@ -88,10 +88,12 @@ export class FrameImpl implements StackTrace.StackTrace.Frame {
88
88
 
89
89
  readonly missingDebugInfo?: StackTrace.StackTrace.MissingDebugInfo;
90
90
  readonly rawName?: string;
91
+ readonly isWasm?: boolean;
91
92
 
92
93
  constructor(
93
94
  url: string|undefined, uiSourceCode: Workspace.UISourceCode.UISourceCode|undefined, name: string|undefined,
94
- line: number, column: number, missingDebugInfo?: StackTrace.StackTrace.MissingDebugInfo, rawName?: string) {
95
+ line: number, column: number, missingDebugInfo?: StackTrace.StackTrace.MissingDebugInfo, rawName?: string,
96
+ isWasm?: boolean) {
95
97
  this.url = url;
96
98
  this.uiSourceCode = uiSourceCode;
97
99
  this.name = name;
@@ -99,6 +101,7 @@ export class FrameImpl implements StackTrace.StackTrace.Frame {
99
101
  this.column = column;
100
102
  this.missingDebugInfo = missingDebugInfo;
101
103
  this.rawName = rawName;
104
+ this.isWasm = isWasm;
102
105
  }
103
106
  }
104
107
 
@@ -192,7 +195,7 @@ export class ParsedErrorStackFrameImpl implements StackTrace.StackTrace.ParsedEr
192
195
  return this.#evalOrigin;
193
196
  }
194
197
  get isWasm(): boolean|undefined {
195
- return this.#parsedFrameInfo?.isWasm;
198
+ return this.#frame.isWasm;
196
199
  }
197
200
  get wasmModuleName(): string|undefined {
198
201
  return this.#parsedFrameInfo?.wasmModuleName;
@@ -284,6 +287,10 @@ export class DebuggableFrameImpl implements StackTrace.StackTrace.DebuggableFram
284
287
  return this.#frame.rawName;
285
288
  }
286
289
 
290
+ get isWasm(): boolean|undefined {
291
+ return this.#frame.isWasm;
292
+ }
293
+
287
294
  get sdkFrame(): SDK.DebuggerModel.CallFrame {
288
295
  return this.#sdkFrame;
289
296
  }
@@ -48,8 +48,14 @@ export class StackTraceModel extends SDK.SDKModel.SDKModel<unknown> {
48
48
 
49
49
  async createFromProtocolRuntime(stackTrace: Protocol.Runtime.StackTrace, rawFramesToUIFrames: TranslateRawFrames):
50
50
  Promise<StackTrace.StackTrace.StackTrace> {
51
+ const debuggerModel = this.target().model(SDK.DebuggerModel.DebuggerModel);
52
+ const syncFrames = stackTrace.callFrames.map((frame): RawFrame => {
53
+ const isWasm = debuggerModel?.isWasm(frame.scriptId) ?? false;
54
+ return {...frame, isWasm};
55
+ });
56
+
51
57
  const [syncFragment, asyncFragments] = await Promise.all([
52
- this.#createFragment(stackTrace.callFrames, rawFramesToUIFrames),
58
+ this.#createFragment(syncFrames, rawFramesToUIFrames),
53
59
  this.#createAsyncFragments(stackTrace, rawFramesToUIFrames),
54
60
  ]);
55
61
 
@@ -126,6 +132,7 @@ export class StackTraceModel extends SDK.SDKModel.SDKModel<unknown> {
126
132
  functionName: frame.functionName,
127
133
  lineNumber: frame.location().lineNumber,
128
134
  columnNumber: frame.location().columnNumber,
135
+ isWasm: frame.script.isWasm(),
129
136
  })),
130
137
  rawFramesToUIFrames);
131
138
  return new DebuggableFragmentImpl(fragment, pausedDetails.callFrames);
@@ -144,8 +151,14 @@ export class StackTraceModel extends SDK.SDKModel.SDKModel<unknown> {
144
151
  continue;
145
152
  }
146
153
  const model = StackTraceModel.#modelForTarget(target);
154
+ const targetDebuggerModel = target.model(SDK.DebuggerModel.DebuggerModel);
155
+ const asyncFrames = asyncStackTrace.callFrames.map((frame): RawFrame => {
156
+ const isWasm = targetDebuggerModel?.isWasm(frame.scriptId) ?? false;
157
+ return {...frame, isWasm};
158
+ });
159
+
147
160
  const asyncFragmentPromise =
148
- model.#createFragment(asyncStackTrace.callFrames, rawFramesToUIFrames)
161
+ model.#createFragment(asyncFrames, rawFramesToUIFrames)
149
162
  .then(fragment => new AsyncFragmentImpl(asyncStackTrace.description ?? '', fragment));
150
163
  asyncFragments.push(asyncFragmentPromise);
151
164
  }
@@ -201,7 +214,7 @@ export class StackTraceModel extends SDK.SDKModel.SDKModel<unknown> {
201
214
  node.frames = uiFrames[i++].map(
202
215
  frame => new FrameImpl(
203
216
  frame.url, frame.uiSourceCode, frame.name, frame.line, frame.column, frame.missingDebugInfo,
204
- node.rawFrame.functionName));
217
+ node.rawFrame.functionName, node.rawFrame.isWasm));
205
218
 
206
219
  if (node.parsedFrameInfo?.evalOrigin) {
207
220
  node.evalOrigin = evalOrigins[evalI++];
@@ -243,7 +256,7 @@ async function translateEvalOrigin(
243
256
  const frames = uiFrames[0].map(
244
257
  frame => new FrameImpl(
245
258
  frame.url, frame.uiSourceCode, frame.name, frame.line, frame.column, frame.missingDebugInfo,
246
- rawFrame.functionName));
259
+ rawFrame.functionName, rawFrame.isWasm));
247
260
 
248
261
  let parentEvalOrigin: EvalOrigin|undefined;
249
262
  if (rawFrame.parsedFrameInfo?.evalOrigin) {
@@ -11,7 +11,6 @@ export interface ParsedFrameInfo {
11
11
  readonly isConstructor?: boolean;
12
12
  readonly isEval?: boolean;
13
13
  readonly evalOrigin?: RawFrame;
14
- readonly isWasm?: boolean;
15
14
  readonly wasmModuleName?: string;
16
15
  readonly wasmFunctionIndex?: number;
17
16
  readonly typeName?: string;
@@ -30,6 +29,7 @@ export interface RawFrame {
30
29
  readonly columnNumber: number;
31
30
 
32
31
  readonly parsedFrameInfo?: ParsedFrameInfo;
32
+ readonly isWasm?: boolean;
33
33
  }
34
34
 
35
35
  /**
@@ -362,23 +362,22 @@ function getMarkdownRenderer(conversation?: AiAssistanceModel.AiConversation.AiC
362
362
  const context = conversation?.selectedContext;
363
363
 
364
364
  if (context instanceof AiAssistanceModel.PerformanceAgent.PerformanceTraceContext) {
365
- if (!context.external) {
366
- const focus = context.getItem();
367
- return new PerformanceAgentMarkdownRenderer(
368
- focus.parsedTrace.data.Meta.mainFrameId, focus.lookupEvent.bind(focus));
369
- }
370
- } else if (conversation?.type === AiAssistanceModel.AiHistoryStorage.ConversationType.PERFORMANCE) {
365
+ const focus = context.getItem();
366
+ return new PerformanceAgentMarkdownRenderer(focus.parsedTrace.data.Meta.mainFrameId, focus.lookupEvent.bind(focus));
367
+ }
368
+ if (conversation?.type === AiAssistanceModel.AiHistoryStorage.ConversationType.PERFORMANCE) {
371
369
  // Handle historical conversations (can't linkify anything).
372
370
  return new PerformanceAgentMarkdownRenderer();
373
- } else if (
374
- Greendev.Prototypes.instance().isEnabled('emulationCapabilities') &&
371
+ }
372
+ if (Greendev.Prototypes.instance().isEnabled('emulationCapabilities') &&
375
373
  conversation?.type === AiAssistanceModel.AiHistoryStorage.ConversationType.STYLING &&
376
374
  SDK.TargetManager.TargetManager.instance().primaryPageTarget()?.model(SDK.DOMModel.DOMModel)) {
377
375
  const domModel = SDK.TargetManager.TargetManager.instance().primaryPageTarget()?.model(SDK.DOMModel.DOMModel);
378
376
  const resourceTreeModel = domModel?.target().model(SDK.ResourceTreeModel.ResourceTreeModel);
379
377
  const mainFrameId = resourceTreeModel?.mainFrame?.id;
380
378
  return new StylingAgentMarkdownRenderer(mainFrameId);
381
- } else if (conversation?.type === AiAssistanceModel.AiHistoryStorage.ConversationType.ACCESSIBILITY) {
379
+ }
380
+ if (conversation?.type === AiAssistanceModel.AiHistoryStorage.ConversationType.ACCESSIBILITY) {
382
381
  const domModel = SDK.TargetManager.TargetManager.instance().primaryPageTarget()?.model(SDK.DOMModel.DOMModel);
383
382
  const mainDocumentURL = domModel?.existingDocument()?.documentURL;
384
383
  return new AccessibilityAgentMarkdownRenderer(mainDocumentURL);
@@ -570,11 +569,11 @@ function defaultView(input: ViewInput, output: PanelViewOutput, target: HTMLElem
570
569
  // clang-format on
571
570
  }
572
571
 
573
- function createNodeContext(node: SDK.DOMModel.DOMNode|null): AiAssistanceModel.StylingAgent.NodeContext|null {
572
+ function createDOMNodeContext(node: SDK.DOMModel.DOMNode|null): AiAssistanceModel.DOMNodeContext.DOMNodeContext|null {
574
573
  if (!node) {
575
574
  return null;
576
575
  }
577
- return new AiAssistanceModel.StylingAgent.NodeContext(node);
576
+ return new AiAssistanceModel.DOMNodeContext.DOMNodeContext(node);
578
577
  }
579
578
 
580
579
  function createFileContext(file: Workspace.UISourceCode.UISourceCode|null): AiAssistanceModel.FileAgent.FileContext|
@@ -658,7 +657,7 @@ export class AiAssistancePanel extends UI.Panel.Panel {
658
657
  // NodeJS debugging does not have Elements panel, thus this action might not exist.
659
658
  #toggleSearchElementAction?: UI.ActionRegistration.Action;
660
659
  #aidaClient: Host.AidaClient.AidaClient;
661
- #conversationSummaryAgent?: AiAssistanceModel.ConversationSummaryAgent.ConversationSummaryAgent;
660
+ #conversationSummary?: AiAssistanceModel.ConversationSummary.ConversationSummary;
662
661
  #viewOutput: PanelViewOutput = {};
663
662
  #serverSideLoggingEnabled = isAiAssistanceServerSideLoggingEnabled();
664
663
  #aiAssistanceEnabledSetting: Common.Settings.Setting<boolean>|undefined;
@@ -668,7 +667,7 @@ export class AiAssistancePanel extends UI.Panel.Panel {
668
667
  #conversation?: AiAssistanceModel.AiConversation.AiConversation;
669
668
 
670
669
  #selectedFile: AiAssistanceModel.FileAgent.FileContext|null = null;
671
- #selectedElement: AiAssistanceModel.StylingAgent.NodeContext|null = null;
670
+ #selectedElement: AiAssistanceModel.DOMNodeContext.DOMNodeContext|null = null;
672
671
  #selectedPerformanceTrace: AiAssistanceModel.PerformanceAgent.PerformanceTraceContext|null = null;
673
672
  #selectedRequest: AiAssistanceModel.NetworkAgent.RequestContext|null = null;
674
673
 
@@ -785,17 +784,16 @@ export class AiAssistancePanel extends UI.Panel.Panel {
785
784
  markdownRenderer,
786
785
  conversationMarkdown: this.#conversation.getConversationMarkdown(),
787
786
  generateConversationSummary: async (markdown: string) => {
788
- if (!this.#conversationSummaryAgent) {
789
- this.#conversationSummaryAgent = new AiAssistanceModel.ConversationSummaryAgent.ConversationSummaryAgent({
787
+ if (!this.#conversationSummary) {
788
+ this.#conversationSummary = new AiAssistanceModel.ConversationSummary.ConversationSummary({
790
789
  aidaClient: this.#aidaClient,
791
790
  serverSideLoggingEnabled: this.#serverSideLoggingEnabled,
792
791
  });
793
792
  }
794
- return await this.#conversationSummaryAgent.summarizeConversation(markdown);
793
+ return await this.#conversationSummary.summarizeConversation(markdown);
795
794
  },
796
- onTextSubmit: async (
797
- text: string, imageInput?: Host.AidaClient.Part,
798
- multimodalInputType?: AiAssistanceModel.AiAgent.MultimodalInputType) => {
795
+ onTextSubmit: async (text: string, imageInput?: Host.AidaClient.Part,
796
+ multimodalInputType?: AiAssistanceModel.AiAgent.MultimodalInputType) => {
799
797
  const submit = (): void => {
800
798
  Host.userMetrics.actionTaken(Host.UserMetrics.Action.AiAssistanceQuerySubmitted);
801
799
  void this.#startConversation(text, imageInput, multimodalInputType);
@@ -1117,7 +1115,7 @@ export class AiAssistancePanel extends UI.Panel.Panel {
1117
1115
  this.#viewOutput.chatView?.focusTextInput();
1118
1116
  void this.#handleAidaAvailabilityChange();
1119
1117
  this.#selectedElement =
1120
- createNodeContext(selectedElementFilter(UI.Context.Context.instance().flavor(SDK.DOMModel.DOMNode)));
1118
+ createDOMNodeContext(selectedElementFilter(UI.Context.Context.instance().flavor(SDK.DOMModel.DOMNode)));
1121
1119
  this.#selectedRequest =
1122
1120
  createRequestContext(UI.Context.Context.instance().flavor(SDK.NetworkRequest.NetworkRequest));
1123
1121
  this.#selectedPerformanceTrace =
@@ -1225,7 +1223,7 @@ export class AiAssistancePanel extends UI.Panel.Panel {
1225
1223
  return;
1226
1224
  }
1227
1225
 
1228
- this.#selectedElement = createNodeContext(selectedElementFilter(ev.data));
1226
+ this.#selectedElement = createDOMNodeContext(selectedElementFilter(ev.data));
1229
1227
  this.#updateConversationState(this.#conversation);
1230
1228
  };
1231
1229
 
@@ -1576,6 +1574,11 @@ export class AiAssistancePanel extends UI.Panel.Panel {
1576
1574
  targetConversationType = AiAssistanceModel.AiHistoryStorage.ConversationType.FILE;
1577
1575
  break;
1578
1576
  }
1577
+ case 'ai-assistance.storage-floating-button': {
1578
+ Host.userMetrics.actionTaken(Host.UserMetrics.Action.AiAssistanceOpenedFromStoragePanelFloatingButton);
1579
+ targetConversationType = AiAssistanceModel.AiHistoryStorage.ConversationType.STORAGE;
1580
+ break;
1581
+ }
1579
1582
  }
1580
1583
 
1581
1584
  if (!targetConversationType) {
@@ -1727,7 +1730,7 @@ export class AiAssistancePanel extends UI.Panel.Panel {
1727
1730
  #handleConversationContextChange = (data: unknown): void => {
1728
1731
  if (data instanceof AiAssistanceModel.FileAgent.FileContext) {
1729
1732
  this.#selectedFile = data;
1730
- } else if (data instanceof AiAssistanceModel.StylingAgent.NodeContext) {
1733
+ } else if (data instanceof AiAssistanceModel.DOMNodeContext.DOMNodeContext) {
1731
1734
  this.#selectedElement = data;
1732
1735
  } else if (data instanceof AiAssistanceModel.NetworkAgent.RequestContext) {
1733
1736
  this.#selectedRequest = data;
@@ -297,3 +297,19 @@ UI.ActionRegistration.registerActionExtension({
297
297
  },
298
298
  condition: config => isFileAgentFeatureAvailable(config) && !isPolicyRestricted(config) && !isGeoRestricted(config),
299
299
  });
300
+
301
+ UI.ActionRegistration.registerActionExtension({
302
+ actionId: 'ai-assistance.storage-floating-button',
303
+ contextTypes(): [] {
304
+ return [];
305
+ },
306
+ category: UI.ActionRegistration.ActionCategory.GLOBAL,
307
+ title: i18nAiBrandedString(UIStrings.debugWithGemini, UIStrings.debugWithAi),
308
+ configurableBindings: false,
309
+ async loadActionDelegate() {
310
+ const AiAssistance = await loadAiAssistanceModule();
311
+ return new AiAssistance.ActionDelegate();
312
+ },
313
+ condition: config =>
314
+ isStorageAgentFeatureAvailable(config) && !isPolicyRestricted(config) && !isGeoRestricted(config),
315
+ });
@@ -174,7 +174,7 @@ function getContextRemoveLabel(context: AiAssistanceModel.AiAgent.ConversationCo
174
174
  if (context instanceof AiAssistanceModel.FileAgent.FileContext) {
175
175
  return lockedString(UIStringsNotTranslate.removeContextFile);
176
176
  }
177
- if (context instanceof AiAssistanceModel.StylingAgent.NodeContext) {
177
+ if (context instanceof AiAssistanceModel.DOMNodeContext.DOMNodeContext) {
178
178
  return lockedString(UIStringsNotTranslate.removeContextElement);
179
179
  }
180
180
  if (context instanceof AiAssistanceModel.NetworkAgent.RequestContext) {
@@ -336,7 +336,7 @@ export const DEFAULT_VIEW = (input: ViewInput, _output: ViewOutput, target: HTML
336
336
  })}
337
337
  >
338
338
  ${
339
- input.context instanceof AiAssistanceModel.StylingAgent.NodeContext ?
339
+ input.context instanceof AiAssistanceModel.DOMNodeContext.DOMNodeContext ?
340
340
  html`
341
341
  <devtools-widget
342
342
  class="title"
@@ -14,9 +14,9 @@ import * as SDK from '../../../core/sdk/sdk.js';
14
14
  import type * as Protocol from '../../../generated/protocol.js';
15
15
  import type {
16
16
  AiWidget, BottomUpTreeAiWidget, ComputedStyleAiWidget, CoreVitalsAiWidget, DomTreeAiWidget, LighthouseReportAiWidget,
17
- NetworkRequestGeneralHeadersAiWidget, PerfInsightAiWidget, PerformanceTraceAiWidget, SourceCodeAiWidget,
18
- SourceFileAiWidget, SourceFilesListAiWidget, StylePropertiesAiWidget, TimelineEventSummaryAiWidget,
19
- TimelineRangeSummaryAiWidget} from '../../../models/ai_assistance/agents/AiAgent.js';
17
+ NetworkRequestGeneralHeadersAiWidget, NetworkRequestsListAiWidget, PerfInsightAiWidget, PerformanceTraceAiWidget,
18
+ SourceCodeAiWidget, SourceFileAiWidget, SourceFilesListAiWidget, StylePropertiesAiWidget,
19
+ TimelineEventSummaryAiWidget, TimelineRangeSummaryAiWidget} from '../../../models/ai_assistance/agents/AiAgent.js';
20
20
  import * as AiAssistanceModel from '../../../models/ai_assistance/ai_assistance.js';
21
21
  import * as ComputedStyle from '../../../models/computed_style/computed_style.js';
22
22
  import * as Formatter from '../../../models/formatter/formatter.js';
@@ -30,6 +30,7 @@ import * as Buttons from '../../../ui/components/buttons/buttons.js';
30
30
  import * as Input from '../../../ui/components/input/input.js';
31
31
  import type * as MarkdownView from '../../../ui/components/markdown_view/markdown_view.js';
32
32
  import type {MarkdownLitRenderer} from '../../../ui/components/markdown_view/MarkdownView.js';
33
+ import * as Snackbars from '../../../ui/components/snackbars/snackbars.js';
33
34
  import * as UIHelpers from '../../../ui/helpers/helpers.js';
34
35
  import * as UI from '../../../ui/legacy/legacy.js';
35
36
  import * as Lit from '../../../ui/lit/lit.js';
@@ -414,6 +415,14 @@ const UIStringsNotTranslate = {
414
415
  * @description Title for the character set declaration widget.
415
416
  */
416
417
  characterSet: 'Character set declaration',
418
+ /**
419
+ * @description Title for the network requests list widget.
420
+ */
421
+ networkRequests: 'Network requests',
422
+ /**
423
+ * @description Accessible label for the reveal button in the network requests list widget.
424
+ */
425
+ revealFirstNetworkRequest: 'Reveal first network request in Network panel',
417
426
  /**
418
427
  * @description Title for the source files list widget.
419
428
  */
@@ -1300,7 +1309,12 @@ function renderWidgetResponse(response: WidgetMakerResponse|null): Lit.LitTempla
1300
1309
  if (response === null) {
1301
1310
  return;
1302
1311
  }
1303
- void Common.Revealer.reveal(response?.revealable);
1312
+ Common.Revealer.reveal(response?.revealable).catch((error: Error) => {
1313
+ if (!error.message) {
1314
+ return;
1315
+ }
1316
+ Snackbars.Snackbar.Snackbar.show({message: error.message});
1317
+ });
1304
1318
  }
1305
1319
 
1306
1320
  const classes = Lit.Directives.classMap({
@@ -1466,6 +1480,79 @@ async function makeSourceFilesListWidget(widgetData: SourceFilesListAiWidget): P
1466
1480
  };
1467
1481
  }
1468
1482
 
1483
+ const expandedNetworkRequestsWidgets = new WeakSet<NetworkRequestsListAiWidget>();
1484
+
1485
+ // A widget with a table of the list of network requests sent to the agent.
1486
+ // Only show 15 requests maximum in collapsed version. The rest of the requests
1487
+ // will be hidden unless the user clicks "Show all".
1488
+ async function makeNetworkRequestsListWidget(widgetData: NetworkRequestsListAiWidget):
1489
+ Promise<WidgetMakerResponse|null> {
1490
+ const requests = widgetData.data.requests;
1491
+ if (requests.length === 0) {
1492
+ return null;
1493
+ }
1494
+
1495
+ const isExpanded = expandedNetworkRequestsWidgets.has(widgetData);
1496
+ // We only want just expanded widget to be expanded, if the user closed and reopened the walkthrought, the widget should be collapsed again.
1497
+ // Therefore, after rendering the widget, we remove the widget from the set of expanded widgets so that it is collapsed on next render.
1498
+ if (isExpanded) {
1499
+ expandedNetworkRequestsWidgets.delete(widgetData);
1500
+ }
1501
+ const displayedRequests = isExpanded ? requests : requests.slice(0, 15);
1502
+
1503
+ // The table contains same fields as the ones sent to the agent.
1504
+ // clang-format off
1505
+ const renderedWidget = html`
1506
+ <div class="network-requests-widget">
1507
+ <devtools-data-grid striped inline>
1508
+ <table>
1509
+ <tr>
1510
+ <th id="name" weight="4">${i18n.i18n.lockedString('Name')}</th>
1511
+ <th id="status" weight="1">${i18n.i18n.lockedString('Status')}</th>
1512
+ <th id="size" weight="1">${i18n.i18n.lockedString('Size')}</th>
1513
+ <th id="time" weight="1">${i18n.i18n.lockedString('Time')}</th>
1514
+ </tr>
1515
+ ${displayedRequests.map(request => html`
1516
+ <tr>
1517
+ <td>${request.name()}</td>
1518
+ <td>${request.statusCode}</td>
1519
+ <td>${i18n.ByteUtilities.formatBytesToKb(request.transferSize)}</td>
1520
+ <td>${i18n.TimeUtilities.secondsToString(request.duration)}</td>
1521
+ </tr>
1522
+ `)}
1523
+ </table>
1524
+ </devtools-data-grid>
1525
+ ${!isExpanded && requests.length > 15 ? html`
1526
+ <div class="show-all-container">
1527
+ <button class="show-all-widget-requests-button text-button"
1528
+ jslog=${VisualLogging.action('show-all-widget-requests-button').track({click: true})}
1529
+ @click=${(e: Event) => {
1530
+ expandedNetworkRequestsWidgets.add(widgetData);
1531
+ const widgetEl = (e.target as HTMLElement).closest('.widget');
1532
+ if (widgetEl) {
1533
+ const widget = UI.Widget.Widget.get(widgetEl) as ChatMessage;
1534
+ if (widget && widget.performUpdate) {
1535
+ void widget.performUpdate();
1536
+ }
1537
+ }
1538
+ }}>
1539
+ ${i18n.i18n.lockedString(`Show all ${requests.length} network requests`)}
1540
+ </button>
1541
+ </div>
1542
+ ` : Lit.nothing}
1543
+ </div>
1544
+ `;
1545
+ // clang-format on
1546
+
1547
+ return {
1548
+ renderedWidget,
1549
+ title: lockedString(UIStringsNotTranslate.networkRequests),
1550
+ revealable: requests[0],
1551
+ accessibleRevealLabel: lockedString(UIStringsNotTranslate.revealFirstNetworkRequest),
1552
+ jslogContext: 'network-requests-list-widget',
1553
+ };
1554
+ }
1555
+
1469
1556
  function renderNetworkRequestPreview(networkRequest: NonNullable<DomTreeAiWidget['data']['networkRequest']>):
1470
1557
  Lit.TemplateResult {
1471
1558
  const filename = networkRequest.url.split('/').pop() || networkRequest.url;
@@ -1583,6 +1670,8 @@ export function getWidgetSignature(widget: AiWidget): string {
1583
1670
  return `${widget.name}:${widget.data.request.requestId()}`;
1584
1671
  case 'SOURCE_CODE':
1585
1672
  return `${widget.name}:${widget.data.url}:${widget.data.line ?? ''}:${widget.data.column ?? ''}`;
1673
+ case 'NETWORK_REQUESTS_LIST':
1674
+ return `${widget.name}:${widget.data.requests.map(r => r.requestId()).join(',')}`;
1586
1675
  default:
1587
1676
  Platform.assertNever(widget, 'Unknown AiWidget name');
1588
1677
  }
@@ -1672,6 +1761,9 @@ async function renderWidgets(
1672
1761
  case 'SOURCE_FILES_LIST':
1673
1762
  response = await makeSourceFilesListWidget(widgetData);
1674
1763
  break;
1764
+ case 'NETWORK_REQUESTS_LIST':
1765
+ response = await makeNetworkRequestsListWidget(widgetData);
1766
+ break;
1675
1767
  case 'LIGHTHOUSE_REPORT':
1676
1768
  response = await makeLighthouseReportWidget(widgetData);
1677
1769
  break;
@@ -624,4 +624,10 @@
624
624
  display: none;
625
625
  }
626
626
  }
627
+
628
+ .network-requests-widget {
629
+ display: flex;
630
+ flex-direction: column;
631
+ gap: var(--sys-size-4);
632
+ }
627
633
  }
@@ -205,6 +205,10 @@ export class DOMStorageItemsView extends KeyValueStorageItemsView {
205
205
  UI.Context.Context.instance().setFlavor(AiAssistanceModel.StorageItem.StorageItem, storageItem);
206
206
  }
207
207
 
208
+ protected override isAiButtonEnabled(): boolean {
209
+ return UI.ActionRegistry.ActionRegistry.instance().hasAction('ai-assistance.storage-floating-button');
210
+ }
211
+
208
212
  protected removeItem(key: string): void {
209
213
  this.domStorage?.removeItem(key);
210
214
  }