chrome-devtools-frontend 1.0.1555174 → 1.0.1555430

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 (66) hide show
  1. package/front_end/core/protocol_client/InspectorBackend.ts +1 -1
  2. package/front_end/core/root/Runtime.ts +0 -4
  3. package/front_end/core/sdk/DOMModel.ts +101 -7
  4. package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +1 -1
  5. package/front_end/models/ai_assistance/agents/StylingAgent.ts +1 -1
  6. package/front_end/models/ai_assistance/data_formatters/NetworkRequestFormatter.ts +1 -1
  7. package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.ts +1 -1
  8. package/front_end/{ui/components → models}/annotations/AnnotationRepository.ts +3 -3
  9. package/front_end/models/annotations/README.md +7 -0
  10. package/front_end/models/bindings/DebuggerWorkspaceBinding.ts +8 -0
  11. package/front_end/models/stack_trace/StackTrace.ts +13 -2
  12. package/front_end/models/stack_trace/StackTraceImpl.ts +81 -6
  13. package/front_end/models/stack_trace/StackTraceModel.ts +35 -3
  14. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +45 -4
  15. package/front_end/panels/ai_assistance/components/ArtifactsViewer.ts +57 -0
  16. package/front_end/panels/ai_assistance/components/ChatView.ts +1 -0
  17. package/front_end/panels/ai_assistance/components/artifactsViewer.css +10 -0
  18. package/front_end/panels/application/preloading/PreloadingView.ts +12 -6
  19. package/front_end/panels/application/preloading/components/PreloadingDetailsReportView.ts +230 -237
  20. package/front_end/panels/application/preloading/components/PreloadingGrid.ts +96 -79
  21. package/front_end/panels/application/preloading/components/preloadingGrid.css +26 -29
  22. package/front_end/panels/application/preloading/preloadingView.css +6 -0
  23. package/front_end/panels/common/Annotation.ts +1 -1
  24. package/front_end/panels/common/AnnotationManager.ts +1 -1
  25. package/front_end/panels/common/ExtensionView.ts +1 -0
  26. package/front_end/panels/console/ConsoleContextSelector.ts +74 -9
  27. package/front_end/panels/console/consoleContextSelector.css +31 -29
  28. package/front_end/panels/coverage/coverageListView.css +59 -57
  29. package/front_end/panels/elements/ElementsPanel.ts +1 -1
  30. package/front_end/panels/elements/ElementsTreeElement.ts +39 -1
  31. package/front_end/panels/elements/ElementsTreeOutline.ts +23 -21
  32. package/front_end/panels/elements/TopLayerContainer.ts +26 -91
  33. package/front_end/panels/explain/components/ConsoleInsight.ts +3 -3
  34. package/front_end/panels/network/NetworkItemView.ts +1 -1
  35. package/front_end/panels/network/NetworkLogView.ts +1 -1
  36. package/front_end/panels/network/NetworkPanel.ts +1 -1
  37. package/front_end/panels/recorder/RecorderController.ts +0 -1
  38. package/front_end/panels/security/SecurityPanelSidebar.ts +5 -0
  39. package/front_end/panels/timeline/TimelineUIUtils.ts +5 -8
  40. package/front_end/panels/timeline/components/TimelineSummary.ts +75 -54
  41. package/front_end/panels/timeline/components/insights/BaseInsightComponent.ts +16 -25
  42. package/front_end/panels/timeline/components/insights/Cache.ts +12 -8
  43. package/front_end/panels/timeline/components/insights/DOMSize.ts +25 -21
  44. package/front_end/panels/timeline/components/insights/DuplicatedJavaScript.ts +7 -7
  45. package/front_end/panels/timeline/components/insights/FontDisplay.ts +7 -5
  46. package/front_end/panels/timeline/components/insights/ForcedReflow.ts +11 -9
  47. package/front_end/panels/timeline/components/insights/INPBreakdown.ts +7 -6
  48. package/front_end/panels/timeline/components/insights/ImageDelivery.ts +7 -5
  49. package/front_end/panels/timeline/components/insights/InsightRenderer.ts +20 -18
  50. package/front_end/panels/timeline/components/insights/LCPBreakdown.ts +12 -12
  51. package/front_end/panels/timeline/components/insights/LegacyJavaScript.ts +7 -7
  52. package/front_end/panels/timeline/components/insights/ModernHTTP.ts +7 -5
  53. package/front_end/panels/timeline/components/insights/NetworkDependencyTree.ts +15 -13
  54. package/front_end/panels/timeline/components/insights/RenderBlocking.ts +2 -2
  55. package/front_end/panels/timeline/components/insights/SlowCSSSelector.ts +15 -14
  56. package/front_end/panels/timeline/components/insights/Table.ts +152 -130
  57. package/front_end/panels/timeline/components/insights/ThirdParties.ts +11 -9
  58. package/front_end/panels/timeline/components/timelineSummary.css +58 -57
  59. package/front_end/panels/timeline/thirdPartyTreeView.css +109 -0
  60. package/front_end/panels/timeline/timelineDetailsView.css +2 -4
  61. package/front_end/panels/timeline/timelinePanel.css +0 -110
  62. package/front_end/ui/legacy/TabbedPane.ts +1 -1
  63. package/front_end/ui/legacy/ViewManager.ts +2 -32
  64. package/package.json +1 -1
  65. /package/front_end/{ui/components → models}/annotations/AnnotationType.ts +0 -0
  66. /package/front_end/{ui/components → models}/annotations/annotations.ts +0 -0
@@ -40,7 +40,7 @@ export interface Message {
40
40
  params?: MessageParams|null;
41
41
  }
42
42
 
43
- interface EventMessage extends Message {
43
+ export interface EventMessage extends Message {
44
44
  method: QualifiedName;
45
45
  params?: MessageParams|null;
46
46
  }
@@ -389,7 +389,6 @@ export interface HostConfigFreestyler {
389
389
  multimodal?: boolean;
390
390
  multimodalUploadInput?: boolean;
391
391
  functionCalling?: boolean;
392
- featureName?: string;
393
392
  }
394
393
 
395
394
  export interface HostConfigAiAssistanceNetworkAgent {
@@ -397,7 +396,6 @@ export interface HostConfigAiAssistanceNetworkAgent {
397
396
  temperature: number;
398
397
  enabled: boolean;
399
398
  userTier: string;
400
- featureName?: string;
401
399
  }
402
400
 
403
401
  export interface HostConfigAiAssistancePerformanceAgent {
@@ -405,7 +403,6 @@ export interface HostConfigAiAssistancePerformanceAgent {
405
403
  temperature: number;
406
404
  enabled: boolean;
407
405
  userTier: string;
408
- featureName?: string;
409
406
  }
410
407
 
411
408
  export interface HostConfigAiAssistanceFileAgent {
@@ -413,7 +410,6 @@ export interface HostConfigAiAssistanceFileAgent {
413
410
  temperature: number;
414
411
  enabled: boolean;
415
412
  userTier: string;
416
- featureName?: string;
417
413
  }
418
414
 
419
415
  export interface HostConfigAiCodeCompletion {
@@ -108,7 +108,15 @@ export const ARIA_ATTRIBUTES = new Set<string>([
108
108
  'aria-valuetext',
109
109
  ]);
110
110
 
111
- export class DOMNode {
111
+ export enum DOMNodeEvents {
112
+ TOP_LAYER_INDEX_CHANGED = 'TopLayerIndexChanged',
113
+ }
114
+
115
+ export interface DOMNodeEventTypes {
116
+ [DOMNodeEvents.TOP_LAYER_INDEX_CHANGED]: void;
117
+ }
118
+
119
+ export class DOMNode extends Common.ObjectWrapper.ObjectWrapper<DOMNodeEventTypes> {
112
120
  #domModel: DOMModel;
113
121
  #agent: ProtocolProxyApi.DOMApi;
114
122
  ownerDocument!: DOMDocument|null;
@@ -162,8 +170,14 @@ export class DOMNode {
162
170
  detached = false;
163
171
  #retainedNodes?: Set<Protocol.DOM.BackendNodeId>;
164
172
  #adoptedStyleSheets: AdoptedStyleSheet[] = [];
173
+ /**
174
+ * 1-based index of the node in the top layer. Only set
175
+ * for non-backdrop nodes.
176
+ */
177
+ #topLayerIndex = -1;
165
178
 
166
179
  constructor(domModel: DOMModel) {
180
+ super();
167
181
  this.#domModel = domModel;
168
182
  this.#agent = this.#domModel.getAgent();
169
183
  }
@@ -285,6 +299,18 @@ export class DOMNode {
285
299
  return await (childModel?.requestDocument() || null);
286
300
  }
287
301
 
302
+ setTopLayerIndex(idx: number): void {
303
+ const oldIndex = this.#topLayerIndex;
304
+ this.#topLayerIndex = idx;
305
+ if (oldIndex !== idx) {
306
+ this.dispatchEventToListeners(DOMNodeEvents.TOP_LAYER_INDEX_CHANGED);
307
+ }
308
+ }
309
+
310
+ topLayerIndex(): number {
311
+ return this.#topLayerIndex;
312
+ }
313
+
288
314
  isAdFrameNode(): boolean {
289
315
  if (this.isIframe() && this.#frameOwnerFrameId) {
290
316
  const frame = FrameManager.instance().getFrame(this.#frameOwnerFrameId);
@@ -1203,10 +1229,16 @@ export class DOMNodeShortcut {
1203
1229
  nodeType: number;
1204
1230
  nodeName: string;
1205
1231
  deferredNode: DeferredDOMNode;
1206
- constructor(target: Target, backendNodeId: Protocol.DOM.BackendNodeId, nodeType: number, nodeName: string) {
1232
+ // Shortctus to elements that children of the element this shortcut is for.
1233
+ // Currently, use for backdrop elements in the top layer.«
1234
+ childShortcuts: DOMNodeShortcut[] = [];
1235
+ constructor(
1236
+ target: Target, backendNodeId: Protocol.DOM.BackendNodeId, nodeType: number, nodeName: string,
1237
+ childShortcuts: DOMNodeShortcut[] = []) {
1207
1238
  this.nodeType = nodeType;
1208
1239
  this.nodeName = nodeName;
1209
1240
  this.deferredNode = new DeferredDOMNode(target, backendNodeId);
1241
+ this.childShortcuts = childShortcuts;
1210
1242
  }
1211
1243
  }
1212
1244
 
@@ -1241,6 +1273,9 @@ export class DOMModel extends SDKModel<EventTypes> {
1241
1273
  #frameOwnerNode?: DOMNode|null;
1242
1274
  #loadNodeAttributesTimeout?: number;
1243
1275
  #searchId?: string;
1276
+ #topLayerThrottler = new Common.Throttler.Throttler(100);
1277
+ #topLayerNodes: DOMNode[] = [];
1278
+
1244
1279
  constructor(target: Target) {
1245
1280
  super(target);
1246
1281
 
@@ -1621,10 +1656,6 @@ export class DOMModel extends SDKModel<EventTypes> {
1621
1656
  this.dispatchEventToListeners(Events.AffectedByStartingStylesFlagUpdated, {node});
1622
1657
  }
1623
1658
 
1624
- topLayerElementsUpdated(): void {
1625
- this.dispatchEventToListeners(Events.TopLayerElementsChanged);
1626
- }
1627
-
1628
1659
  pseudoElementRemoved(parentId: Protocol.DOM.NodeId, pseudoElementId: Protocol.DOM.NodeId): void {
1629
1660
  const parent = this.idToDOMNode.get(parentId);
1630
1661
  if (!parent) {
@@ -1730,6 +1761,69 @@ export class DOMModel extends SDKModel<EventTypes> {
1730
1761
  return this.agent.invoke_getTopLayerElements().then(({nodeIds}) => nodeIds);
1731
1762
  }
1732
1763
 
1764
+ topLayerElementsUpdated(): void {
1765
+ void this.#topLayerThrottler.schedule(async () => {
1766
+ // This returns top layer nodes for all local frames.
1767
+ const result = await this.agent.invoke_getTopLayerElements();
1768
+ if (result.getError()) {
1769
+ return;
1770
+ }
1771
+ // Re-set indexes as we re-create top layer nodes list.
1772
+ const previousDocs = new Set<DOMDocument>();
1773
+ for (const node of this.#topLayerNodes) {
1774
+ node.setTopLayerIndex(-1);
1775
+ if (node.ownerDocument) {
1776
+ previousDocs.add(node.ownerDocument);
1777
+ }
1778
+ }
1779
+ this.#topLayerNodes.splice(0);
1780
+ const nodes: DOMNode[] =
1781
+ result.nodeIds.map(id => this.idToDOMNode.get(id)).filter((node): node is DOMNode => Boolean(node));
1782
+ const nodesByDocument = new Map<DOMDocument, DOMNode[]>();
1783
+ for (const node of nodes) {
1784
+ const document = node.ownerDocument;
1785
+ if (!document) {
1786
+ continue;
1787
+ }
1788
+ if (!nodesByDocument.has(document)) {
1789
+ nodesByDocument.set(document, []);
1790
+ }
1791
+ nodesByDocument.get(document)?.push(node);
1792
+ }
1793
+ for (const [document, nodes] of nodesByDocument) {
1794
+ let topLayerIdx = 1;
1795
+ const documentShortcuts = [];
1796
+ for (const [idx, node] of nodes.entries()) {
1797
+ if (node.nodeName() === '::backdrop') {
1798
+ continue;
1799
+ }
1800
+ const childShortcuts = [];
1801
+ const previousNode = result.nodeIds[idx - 1] ? this.idToDOMNode.get(result.nodeIds[idx - 1]) : null;
1802
+ if (previousNode && previousNode.nodeName() === '::backdrop') {
1803
+ childShortcuts.push(
1804
+ new DOMNodeShortcut(this.target(), previousNode.backendNodeId(), 0, previousNode.nodeName()));
1805
+ }
1806
+ const shortcut = new DOMNodeShortcut(this.target(), node.backendNodeId(), 0, node.nodeName(), childShortcuts);
1807
+ node.setTopLayerIndex(topLayerIdx++);
1808
+ this.#topLayerNodes.push(node);
1809
+ documentShortcuts.push(shortcut);
1810
+ previousDocs.delete(document);
1811
+ }
1812
+ this.dispatchEventToListeners(Events.TopLayerElementsChanged, {
1813
+ document,
1814
+ documentShortcuts,
1815
+ });
1816
+ }
1817
+ // Emit empty events for documents that are no longer in the top layer.
1818
+ for (const document of previousDocs) {
1819
+ this.dispatchEventToListeners(Events.TopLayerElementsChanged, {
1820
+ document,
1821
+ documentShortcuts: [],
1822
+ });
1823
+ }
1824
+ });
1825
+ }
1826
+
1733
1827
  getDetachedDOMNodes(): Promise<Protocol.DOM.DetachedElementInfo[]|null> {
1734
1828
  return this.agent.invoke_getDetachedDomNodes().then(({detachedNodes}) => detachedNodes);
1735
1829
  }
@@ -1823,7 +1917,7 @@ export interface EventTypes {
1823
1917
  [Events.ChildNodeCountUpdated]: DOMNode;
1824
1918
  [Events.DistributedNodesChanged]: DOMNode;
1825
1919
  [Events.MarkersChanged]: DOMNode;
1826
- [Events.TopLayerElementsChanged]: void;
1920
+ [Events.TopLayerElementsChanged]: {document: DOMDocument, documentShortcuts: DOMNodeShortcut[]};
1827
1921
  [Events.ScrollableFlagUpdated]: {node: DOMNode};
1828
1922
  [Events.AffectedByStartingStylesFlagUpdated]: {node: DOMNode};
1829
1923
  [Events.AdoptedStyleSheetsModified]: DOMNode;
@@ -9,7 +9,7 @@ import * as Platform from '../../../core/platform/platform.js';
9
9
  import * as Root from '../../../core/root/root.js';
10
10
  import * as SDK from '../../../core/sdk/sdk.js';
11
11
  import * as Tracing from '../../../services/tracing/tracing.js';
12
- import * as Annotations from '../../../ui/components/annotations/annotations.js';
12
+ import * as Annotations from '../../annotations/annotations.js';
13
13
  import * as SourceMapScopes from '../../source_map_scopes/source_map_scopes.js';
14
14
  import * as Trace from '../../trace/trace.js';
15
15
  import {
@@ -8,7 +8,7 @@ import * as Platform from '../../../core/platform/platform.js';
8
8
  import * as Root from '../../../core/root/root.js';
9
9
  import * as SDK from '../../../core/sdk/sdk.js';
10
10
  import type * as Protocol from '../../../generated/protocol.js';
11
- import * as Annotations from '../../../ui/components/annotations/annotations.js';
11
+ import * as Annotations from '../../annotations/annotations.js';
12
12
  import {ChangeManager} from '../ChangeManager.js';
13
13
  import {debugLog} from '../debug.js';
14
14
  import {EvaluateAction, formatError, SideEffectError} from '../EvaluateAction.js';
@@ -3,7 +3,7 @@
3
3
  // found in the LICENSE file.
4
4
 
5
5
  import type * as SDK from '../../../core/sdk/sdk.js';
6
- import * as Annotations from '../../../ui/components/annotations/annotations.js';
6
+ import * as Annotations from '../../annotations/annotations.js';
7
7
  import * as Logs from '../../logs/logs.js';
8
8
  import * as NetworkTimeCalculator from '../../network_time_calculator/network_time_calculator.js';
9
9
  import * as TextUtils from '../../text_utils/text_utils.js';
@@ -4,7 +4,7 @@
4
4
 
5
5
  import type * as Platform from '../../../core/platform/platform.js';
6
6
  import type * as Protocol from '../../../generated/protocol.js';
7
- import * as Annotations from '../../../ui/components/annotations/annotations.js';
7
+ import * as Annotations from '../../annotations/annotations.js';
8
8
  import * as CrUXManager from '../../crux-manager/crux-manager.js';
9
9
  import type * as SourceMapScopes from '../../source_map_scopes/source_map_scopes.js';
10
10
  import * as Trace from '../../trace/trace.js';
@@ -2,9 +2,9 @@
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 * as Common from '../../../core/common/common.js';
6
- import * as Root from '../../../core/root/root.js';
7
- import type * as SDK from '../../../core/sdk/sdk.js';
5
+ import * as Common from '../../core/common/common.js';
6
+ import * as Root from '../../core/root/root.js';
7
+ import type * as SDK from '../../core/sdk/sdk.js';
8
8
 
9
9
  import {AnnotationType} from './AnnotationType.js';
10
10
 
@@ -0,0 +1,7 @@
1
+ # Annotations Model
2
+
3
+ **IMPORTANT**: this model exists because on the GreenDev project we prototyped a build of DevTools that is able to add annotations to various parts of the UI.
4
+
5
+ This feature is only enabled when Chromium is run with the GreenDev feature flag enabled. There are currently NO PLANS to ship this code to production.
6
+
7
+ If you are reading this and you want to use this code, please speak to jacktfranklin@ first. This code is for a prototype and not in a production state.
@@ -191,6 +191,14 @@ export class DebuggerWorkspaceBinding implements SDK.TargetManager.SDKModelObser
191
191
  return await model.createFromProtocolRuntime(stackTrace, this.#translateRawFrames.bind(this));
192
192
  }
193
193
 
194
+ async createStackTraceFromDebuggerPaused(
195
+ pausedDetails: SDK.DebuggerModel.DebuggerPausedDetails,
196
+ target: SDK.Target.Target): Promise<StackTrace.StackTrace.DebuggableStackTrace> {
197
+ const model =
198
+ target.model(StackTraceImpl.StackTraceModel.StackTraceModel) as StackTraceImpl.StackTraceModel.StackTraceModel;
199
+ return await model.createFromDebuggerPaused(pausedDetails, this.#translateRawFrames.bind(this));
200
+ }
201
+
194
202
  async createLiveLocation(
195
203
  rawLocation: SDK.DebuggerModel.Location, updateDelegate: (arg0: LiveLocation) => Promise<void>,
196
204
  locationPool: LiveLocationPool): Promise<Location|null> {
@@ -6,8 +6,11 @@ import type * as Common from '../../core/common/common.js';
6
6
  import type * as SDK from '../../core/sdk/sdk.js';
7
7
  import type * as Workspace from '../workspace/workspace.js';
8
8
 
9
- export interface StackTrace extends Common.EventTarget.EventTarget<EventTypes> {
10
- readonly syncFragment: Fragment;
9
+ export type StackTrace = BaseStackTrace<Fragment>;
10
+ export type DebuggableStackTrace = BaseStackTrace<DebuggableFragment>;
11
+
12
+ export interface BaseStackTrace<SyncFragmentT extends Fragment> extends Common.EventTarget.EventTarget<EventTypes> {
13
+ readonly syncFragment: SyncFragmentT;
11
14
  readonly asyncFragments: readonly AsyncFragment[];
12
15
  }
13
16
 
@@ -19,6 +22,10 @@ export interface AsyncFragment extends Fragment {
19
22
  readonly description: string;
20
23
  }
21
24
 
25
+ export interface DebuggableFragment {
26
+ readonly frames: readonly DebuggableFrame[];
27
+ }
28
+
22
29
  export interface Frame {
23
30
  readonly url?: string;
24
31
  readonly uiSourceCode?: Workspace.UISourceCode.UISourceCode;
@@ -29,6 +36,10 @@ export interface Frame {
29
36
  readonly missingDebugInfo?: MissingDebugInfo;
30
37
  }
31
38
 
39
+ export interface DebuggableFrame extends Frame {
40
+ readonly sdkFrame: SDK.DebuggerModel.CallFrame;
41
+ }
42
+
32
43
  export const enum MissingDebugInfoType {
33
44
  /** No debug information at all for the call frame */
34
45
  NO_INFO = 'NO_INFO',
@@ -3,29 +3,36 @@
3
3
  // found in the LICENSE file.
4
4
 
5
5
  import * as Common from '../../core/common/common.js';
6
+ import type * as SDK from '../../core/sdk/sdk.js';
6
7
  import type * as Workspace from '../workspace/workspace.js';
7
8
 
8
9
  import type * as StackTrace from './stack_trace.js';
9
10
  import type {FrameNode} from './Trie.js';
10
11
 
11
- export class StackTraceImpl extends Common.ObjectWrapper.ObjectWrapper<StackTrace.StackTrace.EventTypes> implements
12
- StackTrace.StackTrace.StackTrace {
13
- readonly syncFragment: FragmentImpl;
12
+ export type AnyStackTraceImpl = StackTraceImpl<FragmentImpl|DebuggableFragmentImpl>;
13
+
14
+ export class StackTraceImpl<SyncFragmentT extends FragmentImpl|DebuggableFragmentImpl = FragmentImpl> extends
15
+ Common.ObjectWrapper.ObjectWrapper<StackTrace.StackTrace.EventTypes> implements
16
+ StackTrace.StackTrace.BaseStackTrace<SyncFragmentT> {
17
+ readonly syncFragment: SyncFragmentT;
14
18
  readonly asyncFragments: readonly AsyncFragmentImpl[];
15
19
 
16
- constructor(syncFragment: FragmentImpl, asyncFragments: AsyncFragmentImpl[]) {
20
+ constructor(syncFragment: SyncFragmentT, asyncFragments: AsyncFragmentImpl[]) {
17
21
  super();
18
22
  this.syncFragment = syncFragment;
19
23
  this.asyncFragments = asyncFragments;
20
24
 
21
- syncFragment.stackTraces.add(this);
25
+ const fragment =
26
+ syncFragment instanceof DebuggableFragmentImpl ? syncFragment.fragment : syncFragment as FragmentImpl;
27
+ fragment.stackTraces.add(this);
28
+
22
29
  this.asyncFragments.forEach(asyncFragment => asyncFragment.fragment.stackTraces.add(this));
23
30
  }
24
31
  }
25
32
 
26
33
  export class FragmentImpl implements StackTrace.StackTrace.Fragment {
27
34
  readonly node: FrameNode;
28
- readonly stackTraces = new Set<StackTraceImpl>();
35
+ readonly stackTraces = new Set<AnyStackTraceImpl>();
29
36
 
30
37
  /**
31
38
  * Fragments are deduplicated based on the node.
@@ -83,3 +90,71 @@ export class FrameImpl implements StackTrace.StackTrace.Frame {
83
90
  this.missingDebugInfo = missingDebugInfo;
84
91
  }
85
92
  }
93
+
94
+ /**
95
+ * A DebuggableFragmentImpl wraps an existing FragmentImpl. This is important: We can pause at the
96
+ * same location multiple times and the paused information changes each and everytime while the underlying
97
+ * FragmentImpl will stay the same.
98
+ */
99
+ export class DebuggableFragmentImpl implements StackTrace.StackTrace.DebuggableFragment {
100
+ constructor(readonly fragment: FragmentImpl, private readonly callFrames: SDK.DebuggerModel.CallFrame[]) {
101
+ }
102
+
103
+ get frames(): DebuggableFrameImpl[] {
104
+ const frames: DebuggableFrameImpl[] = [];
105
+
106
+ let index = 0;
107
+ for (const node of this.fragment.node.getCallStack()) {
108
+ for (const frame of node.frames) {
109
+ // Each inlined frame gets the same DebugerModel.CallFrame for debugging.
110
+ frames.push(new DebuggableFrameImpl(frame, this.callFrames[index]));
111
+ }
112
+ index++;
113
+ }
114
+
115
+ return frames;
116
+ }
117
+ }
118
+
119
+ /**
120
+ * A DebuggableFrameImpl wraps an existing FrameImpl. This is important: We can pause at the
121
+ * same location multiple times and the paused information changes each and everytime while the underlying
122
+ * FrameImpl will stay the same.
123
+ */
124
+ export class DebuggableFrameImpl implements StackTrace.StackTrace.DebuggableFrame {
125
+ readonly #frame: FrameImpl;
126
+ readonly #sdkFrame: SDK.DebuggerModel.CallFrame;
127
+
128
+ constructor(frame: FrameImpl, sdkFrame: SDK.DebuggerModel.CallFrame) {
129
+ this.#frame = frame;
130
+ this.#sdkFrame = sdkFrame;
131
+ }
132
+
133
+ get url(): string|undefined {
134
+ return this.#frame.url;
135
+ }
136
+
137
+ get uiSourceCode(): Workspace.UISourceCode.UISourceCode|undefined {
138
+ return this.#frame.uiSourceCode;
139
+ }
140
+
141
+ get name(): string|undefined {
142
+ return this.#frame.name;
143
+ }
144
+
145
+ get line(): number {
146
+ return this.#frame.line;
147
+ }
148
+
149
+ get column(): number {
150
+ return this.#frame.column;
151
+ }
152
+
153
+ get missingDebugInfo(): StackTrace.StackTrace.MissingDebugInfo|undefined {
154
+ return this.#frame.missingDebugInfo;
155
+ }
156
+
157
+ get sdkFrame(): SDK.DebuggerModel.CallFrame {
158
+ return this.#sdkFrame;
159
+ }
160
+ }
@@ -7,7 +7,14 @@ import type * as Protocol from '../../generated/protocol.js';
7
7
 
8
8
  // eslint-disable-next-line @devtools/es-modules-import
9
9
  import * as StackTrace from './stack_trace.js';
10
- import {AsyncFragmentImpl, FragmentImpl, FrameImpl, StackTraceImpl} from './StackTraceImpl.js';
10
+ import {
11
+ type AnyStackTraceImpl,
12
+ AsyncFragmentImpl,
13
+ DebuggableFragmentImpl,
14
+ FragmentImpl,
15
+ FrameImpl,
16
+ StackTraceImpl
17
+ } from './StackTraceImpl.js';
11
18
  import {type FrameNode, type RawFrame, Trie} from './Trie.js';
12
19
 
13
20
  /**
@@ -45,10 +52,21 @@ export class StackTraceModel extends SDK.SDKModel.SDKModel<unknown> {
45
52
  return new StackTraceImpl(syncFragment, asyncFragments);
46
53
  }
47
54
 
55
+ async createFromDebuggerPaused(
56
+ pausedDetails: SDK.DebuggerModel.DebuggerPausedDetails,
57
+ rawFramesToUIFrames: TranslateRawFrames): Promise<StackTrace.StackTrace.DebuggableStackTrace> {
58
+ const [syncFragment, asyncFragments] = await Promise.all([
59
+ this.#createDebuggableFragment(pausedDetails, rawFramesToUIFrames),
60
+ this.#createAsyncFragments(pausedDetails, rawFramesToUIFrames),
61
+ ]);
62
+
63
+ return new StackTraceImpl(syncFragment, asyncFragments);
64
+ }
65
+
48
66
  /** Trigger re-translation of all fragments with the provide script in their call stack */
49
67
  async scriptInfoChanged(script: SDK.Script.Script, translateRawFrames: TranslateRawFrames): Promise<void> {
50
68
  const translatePromises: Array<Promise<unknown>> = [];
51
- let stackTracesToUpdate = new Set<StackTraceImpl>();
69
+ let stackTracesToUpdate = new Set<AnyStackTraceImpl>();
52
70
 
53
71
  for (const fragment of this.#affectedFragments(script)) {
54
72
  // We trigger re-translation only for fragments of leaf-nodes. Any fragment along the ancestor-chain
@@ -75,8 +93,22 @@ export class StackTraceModel extends SDK.SDKModel.SDKModel<unknown> {
75
93
  return fragment;
76
94
  }
77
95
 
96
+ async #createDebuggableFragment(
97
+ pausedDetails: SDK.DebuggerModel.DebuggerPausedDetails,
98
+ rawFramesToUIFrames: TranslateRawFrames): Promise<DebuggableFragmentImpl> {
99
+ const fragment = this.#createFragment(pausedDetails.callFrames.map(frame => ({
100
+ scriptId: frame.script.scriptId,
101
+ url: frame.script.sourceURL,
102
+ functionName: frame.functionName,
103
+ lineNumber: frame.location().lineNumber,
104
+ columnNumber: frame.location().columnNumber,
105
+ })));
106
+ await this.#translateFragment(fragment, rawFramesToUIFrames);
107
+ return new DebuggableFragmentImpl(fragment, pausedDetails.callFrames);
108
+ }
109
+
78
110
  async #createAsyncFragments(
79
- stackTraceOrPausedEvent: Protocol.Runtime.StackTrace|Protocol.Debugger.PausedEvent,
111
+ stackTraceOrPausedEvent: Protocol.Runtime.StackTrace|SDK.DebuggerModel.DebuggerPausedDetails,
80
112
  rawFramesToUIFrames: TranslateRawFrames): Promise<AsyncFragmentImpl[]> {
81
113
  const asyncFragments: AsyncFragmentImpl[] = [];
82
114
  const translatePromises: Array<Promise<unknown>> = [];
@@ -12,10 +12,10 @@ import * as Root from '../../core/root/root.js';
12
12
  import * as SDK from '../../core/sdk/sdk.js';
13
13
  import * as Protocol from '../../generated/protocol.js';
14
14
  import * as AiAssistanceModel from '../../models/ai_assistance/ai_assistance.js';
15
+ import * as Annotations from '../../models/annotations/annotations.js';
15
16
  import * as Badges from '../../models/badges/badges.js';
16
17
  import * as TextUtils from '../../models/text_utils/text_utils.js';
17
18
  import * as Workspace from '../../models/workspace/workspace.js';
18
- import * as Annotations from '../../ui/components/annotations/annotations.js';
19
19
  import * as Buttons from '../../ui/components/buttons/buttons.js';
20
20
  import * as Snackbars from '../../ui/components/snackbars/snackbars.js';
21
21
  import * as UIHelpers from '../../ui/helpers/helpers.js';
@@ -27,6 +27,7 @@ import * as NetworkPanel from '../network/network.js';
27
27
  import * as TimelinePanel from '../timeline/timeline.js';
28
28
 
29
29
  import aiAssistancePanelStyles from './aiAssistancePanel.css.js';
30
+ import {ArtifactsViewer} from './components/ArtifactsViewer.js';
30
31
  import {
31
32
  type AnswerPart,
32
33
  type ChatMessage,
@@ -294,6 +295,8 @@ interface ToolbarViewInput {
294
295
  onExportConversationClick: () => void;
295
296
  onHelpClick: () => void;
296
297
  onSettingsClick: () => void;
298
+ onArtifactsSidebarToggle: () => void;
299
+ artifactsSidebarVisible: boolean;
297
300
  isLoading: boolean;
298
301
  showChatActions: boolean;
299
302
  showActiveConversationActions: boolean;
@@ -386,6 +389,13 @@ function toolbarView(input: ToolbarViewInput): Lit.LitTemplate {
386
389
  .jslogContext=${'freestyler.settings'}
387
390
  .variant=${Buttons.Button.Variant.TOOLBAR}
388
391
  @click=${input.onSettingsClick}></devtools-button>
392
+ <!-- If the green experiment is enabled, render the artifacts sidebar toggle button -->
393
+ ${Root.Runtime.hostConfig.devToolsGreenDevUi?.enabled ? html`<devtools-button
394
+ title=${i18nString(UIStrings.settings)}
395
+ aria-label=${i18nString(UIStrings.settings)}
396
+ .iconName=${input.artifactsSidebarVisible ? 'left-panel-open' : 'left-panel-close'}
397
+ .variant=${Buttons.Button.Variant.TOOLBAR}
398
+ @click=${input.onArtifactsSidebarToggle}></devtools-button>` : Lit.nothing}
389
399
  </devtools-toolbar>
390
400
  </div>
391
401
  `;
@@ -396,8 +406,9 @@ function defaultView(input: ViewInput, output: PanelViewOutput, target: HTMLElem
396
406
  // clang-format off
397
407
  function renderState(): Lit.TemplateResult {
398
408
  switch (input.state) {
399
- case ViewState.CHAT_VIEW:
400
- return html`<devtools-ai-chat-view
409
+ case ViewState.CHAT_VIEW: {
410
+ const aiChatView = html`
411
+ <devtools-ai-chat-view
401
412
  .props=${input.props}
402
413
  ${Lit.Directives.ref((el: Element | undefined) => {
403
414
  if (!el || !(el instanceof ChatView)) {
@@ -407,6 +418,30 @@ function defaultView(input: ViewInput, output: PanelViewOutput, target: HTMLElem
407
418
  output.chatView = el;
408
419
  })}
409
420
  ></devtools-ai-chat-view>`;
421
+ // If the green experiment is enabled, render the chat view inside
422
+ // a split view to also have an artifacts viewer sidebar.
423
+ if(Root.Runtime.hostConfig.devToolsGreenDevUi?.enabled) {
424
+ return html`
425
+ <devtools-split-view
426
+ direction="column"
427
+ sidebar-visibility=${input.props.isArtifactsSidebarOpen ? 'visible' : 'hidden'}
428
+ sidebar-position="second"
429
+ style="width: 100%;"
430
+ >
431
+ <div slot="main">
432
+ ${aiChatView}
433
+ </div>
434
+ <div slot="sidebar">
435
+ <devtools-widget
436
+ class="fill-panel"
437
+ .widgetConfig=${UI.Widget.widgetConfig(ArtifactsViewer)}
438
+ ></devtools-widget>
439
+ </div>
440
+ </devtools-split-view>`;
441
+ }
442
+
443
+ return aiChatView;
444
+ }
410
445
  case ViewState.EXPLORE_VIEW:
411
446
  return html`<devtools-widget
412
447
  class="fill-panel"
@@ -482,7 +517,7 @@ export class AiAssistancePanel extends UI.Panel.Panel {
482
517
  #selectedElement: AiAssistanceModel.StylingAgent.NodeContext|null = null;
483
518
  #selectedPerformanceTrace: AiAssistanceModel.PerformanceAgent.PerformanceTraceContext|null = null;
484
519
  #selectedRequest: AiAssistanceModel.NetworkAgent.RequestContext|null = null;
485
-
520
+ #isArtifactsSidebarOpen = false;
486
521
  // Messages displayed in the `ChatView` component.
487
522
  #messages: ChatMessage[] = [];
488
523
 
@@ -574,6 +609,7 @@ export class AiAssistancePanel extends UI.Panel.Panel {
574
609
  uploadImageInputEnabled: isAiAssistanceMultimodalUploadInputEnabled() &&
575
610
  this.#conversation.type === AiAssistanceModel.AiHistoryStorage.ConversationType.STYLING,
576
611
  markdownRenderer,
612
+ isArtifactsSidebarOpen: this.#isArtifactsSidebarOpen,
577
613
  onTextSubmit: async (
578
614
  text: string, imageInput?: Host.AidaClient.Part,
579
615
  multimodalInputType?: AiAssistanceModel.AiAgent.MultimodalInputType) => {
@@ -927,6 +963,11 @@ export class AiAssistancePanel extends UI.Panel.Panel {
927
963
  onSettingsClick: () => {
928
964
  void UI.ViewManager.ViewManager.instance().showView('chrome-ai');
929
965
  },
966
+ onArtifactsSidebarToggle: () => {
967
+ this.#isArtifactsSidebarOpen = !this.#isArtifactsSidebarOpen;
968
+ this.requestUpdate();
969
+ },
970
+ artifactsSidebarVisible: this.#isArtifactsSidebarOpen,
930
971
  };
931
972
  }
932
973
 
@@ -0,0 +1,57 @@
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
+ import * as UI from '../../../ui/legacy/legacy.js';
6
+ import * as Lit from '../../../ui/lit/lit.js';
7
+
8
+ import artifactsViewerStyles from './artifactsViewer.css.js';
9
+
10
+ const {html, render} = Lit;
11
+
12
+ export interface ViewInput {
13
+ artifacts: [];
14
+ }
15
+
16
+ export const DEFAULT_VIEW = (
17
+ _input: ViewInput,
18
+ _output: Record<string, unknown>,
19
+ target: HTMLElement,
20
+ ): void => {
21
+ // clang-format off
22
+ render(
23
+ html`
24
+ <style>${artifactsViewerStyles}</style>
25
+ <div>
26
+ Artifacts Viewer
27
+ </div>
28
+ `,
29
+ target
30
+ );
31
+ // clang-format on
32
+ };
33
+
34
+ export type View = typeof DEFAULT_VIEW;
35
+
36
+ export class ArtifactsViewer extends UI.Widget.Widget {
37
+ #view: View;
38
+ constructor(element?: HTMLElement, view = DEFAULT_VIEW) {
39
+ super(element);
40
+ this.#view = view;
41
+ }
42
+
43
+ override wasShown(): void {
44
+ super.wasShown();
45
+ void this.requestUpdate();
46
+ }
47
+
48
+ override performUpdate(): Promise<void>|void {
49
+ this.#view(
50
+ {
51
+ artifacts: [],
52
+ },
53
+ {},
54
+ this.contentElement,
55
+ );
56
+ }
57
+ }