chrome-devtools-frontend 1.0.1608868 → 1.0.1611099

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 (59) hide show
  1. package/eslint.config.mjs +3 -1
  2. package/front_end/core/host/AidaGcaTranslation.ts +13 -0
  3. package/front_end/core/sdk/PreloadingModel.ts +1 -1
  4. package/front_end/entrypoints/heap_snapshot_worker/AllocationProfile.ts +1 -1
  5. package/front_end/entrypoints/heap_snapshot_worker/HeapSnapshot.ts +1 -11
  6. package/front_end/entrypoints/heap_snapshot_worker/HeapSnapshotWorkerDispatcher.ts +1 -1
  7. package/front_end/generated/InspectorBackendCommands.ts +1 -2
  8. package/front_end/generated/SupportedCSSProperties.js +23 -4
  9. package/front_end/generated/protocol-mapping.d.ts +0 -9
  10. package/front_end/generated/protocol-proxy-api.d.ts +0 -7
  11. package/front_end/generated/protocol.ts +0 -20
  12. package/front_end/models/ai_assistance/agents/AccessibilityAgent.ts +12 -3
  13. package/front_end/models/ai_assistance/agents/ContextSelectionAgent.ts +5 -4
  14. package/front_end/models/ai_assistance/agents/ConversationSummaryAgent.ts +24 -1
  15. package/front_end/models/ai_assistance/agents/PerformanceAgent.snapshot.txt +2 -2
  16. package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +41 -22
  17. package/front_end/models/ai_assistance/agents/StylingAgent.snapshot.txt +2 -2
  18. package/front_end/models/ai_assistance/agents/StylingAgent.ts +5 -3
  19. package/front_end/models/ai_assistance/data_formatters/NetworkRequestFormatter.ts +10 -5
  20. package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.snapshot.txt +69 -69
  21. package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.ts +1 -1
  22. package/front_end/models/javascript_metadata/NativeFunctions.js +8 -12
  23. package/front_end/models/live-metrics/LiveMetrics.ts +43 -41
  24. package/front_end/models/trace/EntityMapper.ts +12 -0
  25. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +7 -9
  26. package/front_end/panels/ai_assistance/components/ChatMessage.ts +66 -34
  27. package/front_end/panels/ai_assistance/components/ExportForAgentsDialog.ts +1 -1
  28. package/front_end/panels/ai_assistance/components/WalkthroughView.ts +1 -0
  29. package/front_end/panels/ai_assistance/components/chatMessage.css +15 -0
  30. package/front_end/panels/ai_assistance/components/walkthroughView.css +5 -4
  31. package/front_end/panels/application/ApplicationPanelSidebar.ts +8 -0
  32. package/front_end/panels/application/IndexedDBViews.ts +1 -1
  33. package/front_end/panels/application/ResourcesPanel.ts +8 -0
  34. package/front_end/panels/application/ServiceWorkerCacheViews.ts +1 -1
  35. package/front_end/panels/application/application-meta.ts +11 -0
  36. package/front_end/panels/application/application.ts +1 -0
  37. package/front_end/panels/application/components/StorageMetadataView.ts +31 -7
  38. package/front_end/panels/application/components/backForwardCacheView.css +4 -0
  39. package/front_end/panels/application/preloading/components/PreloadingDetailsReportView.ts +29 -0
  40. package/front_end/panels/elements/ElementsTreeElement.ts +4 -0
  41. package/front_end/panels/lighthouse/LighthousePanel.ts +3 -1
  42. package/front_end/panels/profiler/HeapSnapshotDataGrids.ts +1 -1
  43. package/front_end/panels/profiler/HeapSnapshotGridNodes.ts +1 -1
  44. package/front_end/panels/profiler/HeapSnapshotView.ts +1 -1
  45. package/front_end/panels/timeline/TimelinePanel.ts +2 -6
  46. package/front_end/panels/timeline/utils/Helpers.ts +1 -1
  47. package/front_end/third_party/chromium/README.chromium +1 -1
  48. package/front_end/ui/legacy/InspectorDrawerView.ts +188 -15
  49. package/front_end/ui/legacy/InspectorView.ts +162 -11
  50. package/front_end/ui/legacy/TabbedPane.ts +2 -2
  51. package/front_end/ui/legacy/inspectorDrawerTabbedPane.css +54 -0
  52. package/front_end/ui/visual_logging/KnownContextValues.ts +2 -0
  53. package/front_end/ui/visual_logging/LoggingDriver.ts +3 -3
  54. package/mcp/mcp.ts +2 -1
  55. package/package.json +4 -5
  56. /package/front_end/models/{heap_snapshot_model → heap_snapshot}/ChildrenProvider.ts +0 -0
  57. /package/front_end/models/{heap_snapshot_model → heap_snapshot}/HeapSnapshotModel.ts +0 -0
  58. /package/front_end/models/{heap_snapshot_model → heap_snapshot}/HeapSnapshotProxy.ts +0 -0
  59. /package/front_end/models/{heap_snapshot_model/heap_snapshot_model.ts → heap_snapshot/heap_snapshot.ts} +0 -0
@@ -10,6 +10,8 @@ import * as Helpers from './helpers/helpers.js';
10
10
  import type {ParsedTrace} from './ModelImpl.js';
11
11
  import type * as Types from './types/types.js';
12
12
 
13
+ const mapperCache = new WeakMap<ParsedTrace, EntityMapper>();
14
+
13
15
  export class EntityMapper {
14
16
  #parsedTrace: ParsedTrace;
15
17
  #entityMappings: Handlers.Helpers.EntityMappings;
@@ -30,6 +32,16 @@ export class EntityMapper {
30
32
  this.#thirdPartyEvents = this.#getThirdPartyEvents();
31
33
  }
32
34
 
35
+ static getOrCreate(parsedTrace: ParsedTrace): EntityMapper {
36
+ const cached = mapperCache.get(parsedTrace);
37
+ if (cached) {
38
+ return cached;
39
+ }
40
+ const instance = new EntityMapper(parsedTrace);
41
+ mapperCache.set(parsedTrace, instance);
42
+ return instance;
43
+ }
44
+
33
45
  #findFirstPartyEntity(): Handlers.Helpers.Entity|null {
34
46
  // As a starting point, we consider the first navigation as the 1P.
35
47
  const nav =
@@ -539,15 +539,13 @@ function defaultView(input: ViewInput, output: PanelViewOutput, target: HTMLElem
539
539
  <div slot="main" class="main-view">
540
540
  ${renderState()}
541
541
  </div>
542
- <div slot="sidebar" class="sidebar-view">
543
- ${shouldShowWalkthrough ? html`
544
- <devtools-widget ${widget(WalkthroughView, {
545
- message: input.props.walkthrough.activeSidebarMessage,
546
- isLoading: input.props.isLoading && walkthroughIsForLastMessage,
547
- markdownRenderer: input.props.markdownRenderer,
548
- onToggle: input.props.walkthrough.onToggle,
549
- })}></devtools-widget>` : Lit.nothing}
550
- </div>
542
+ ${shouldShowWalkthrough ? html`
543
+ <devtools-widget slot="sidebar" ${widget(WalkthroughView, {
544
+ message: input.props.walkthrough.activeSidebarMessage,
545
+ isLoading: input.props.isLoading && walkthroughIsForLastMessage,
546
+ markdownRenderer: input.props.markdownRenderer,
547
+ onToggle: input.props.walkthrough.onToggle,
548
+ })}></devtools-widget>` : Lit.nothing}
551
549
  </devtools-split-view>
552
550
  </div>
553
551
  `, target);
@@ -199,7 +199,7 @@ const UIStringsNotTranslate = {
199
199
  /**
200
200
  * @description The title of the button that allows exporting the conversation for agents.
201
201
  */
202
- exportForAgents: 'Copy for your coding agent',
202
+ exportForAgents: 'Copy to coding agent',
203
203
  /**
204
204
  * @description Title for the bottom up thread activity widget.
205
205
  */
@@ -417,6 +417,7 @@ export const DEFAULT_VIEW = (input: ChatMessageViewInput, output: ViewOutput, ta
417
417
  ` : Lit.nothing}
418
418
  ${input.showActions ? renderActions(input, output) : Lit.nothing}
419
419
  </div>
420
+ ${hasAiV2 ? renderSideEffectStepsUI(input, steps) : Lit.nothing}
420
421
  </section>
421
422
  `, target);
422
423
  // clang-format on
@@ -482,7 +483,6 @@ function renderStepCode(step: Step): Lit.LitTemplate {
482
483
  <devtools-code-block
483
484
  .code=${step.code.trim()}
484
485
  .codeLang=${'js'}
485
- .displayLimit=${MAX_NUM_LINES_IN_CODEBLOCK}
486
486
  .displayNotice=${!Boolean(step.output)}
487
487
  .header=${codeHeadingText}
488
488
  .showCopyButton=${true}
@@ -493,7 +493,6 @@ function renderStepCode(step: Step): Lit.LitTemplate {
493
493
  <devtools-code-block
494
494
  .code=${step.output}
495
495
  .codeLang=${'js'}
496
- .displayLimit=${MAX_NUM_LINES_IN_CODEBLOCK}
497
496
  .displayNotice=${true}
498
497
  .header=${lockedString(UIStringsNotTranslate.dataReturned)}
499
498
  .showCopyButton=${false}
@@ -526,7 +525,6 @@ function renderStepDetails({
526
525
  <devtools-code-block
527
526
  .code=${contextDetail.text}
528
527
  .codeLang=${contextDetail.codeLang || ''}
529
- .displayLimit=${MAX_NUM_LINES_IN_CODEBLOCK}
530
528
  .displayNotice=${false}
531
529
  .header=${contextDetail.title}
532
530
  .showCopyButton=${true}
@@ -568,9 +566,14 @@ function renderWalkthroughSidebarButton(
568
566
  const variant = hasOneStepWithWidget && !input.isLoading ? Buttons.Button.Variant.TONAL : Buttons.Button.Variant.TEXT;
569
567
  const icon = AiAssistanceModel.AiUtils.getIconName();
570
568
 
569
+ const toggleContainerClasses = Lit.Directives.classMap({
570
+ 'walkthrough-toggle-container': true,
571
+ // We only apply the widget styling when loading is complete
572
+ 'has-widgets': hasOneStepWithWidget && !input.isLoading,
573
+ });
571
574
  // clang-format off
572
575
  return html`
573
- <div class="walkthrough-toggle-container ${hasOneStepWithWidget ? 'has-widgets' : ''}">
576
+ <div class=${toggleContainerClasses}>
574
577
  ${input.isLoading ?
575
578
  html`<devtools-spinner></devtools-spinner>` :
576
579
  html`<devtools-icon name=${icon}></devtools-icon>`}
@@ -589,9 +592,7 @@ function renderWalkthroughSidebarButton(
589
592
  // the walkthrough open with an alternative message.
590
593
  walkthrough.onOpen(message as ModelChatMessage);
591
594
  }
592
- }}
593
- >
594
- ${title}<devtools-icon class="chevron" .name=${isExpanded ? 'cross' : 'chevron-right'}></devtools-icon>
595
+ }}>${title}<devtools-icon class="chevron" .name=${isExpanded ? 'cross' : 'chevron-right'}></devtools-icon>
595
596
  </devtools-button>
596
597
  </div>
597
598
  `;
@@ -609,7 +610,6 @@ function renderWalkthroughUI(input: ChatMessageViewInput, steps: Step[]): Lit.Li
609
610
  // No steps = no walkthrough UI in the chat view.
610
611
  return Lit.nothing;
611
612
  }
612
- const sideEffectSteps = steps.filter(s => s.requestApproval);
613
613
  // If the walkthrough is in the sidebar, we render a button into the
614
614
  // ChatView to open it.
615
615
  const openWalkThroughSidebarButton =
@@ -622,22 +622,6 @@ function renderWalkthroughUI(input: ChatMessageViewInput, steps: Step[]): Lit.Li
622
622
  input.walkthrough.inlineExpandedMessages.includes(input.message as ModelChatMessage) :
623
623
  (input.walkthrough.isExpanded && input.walkthrough.activeSidebarMessage === input.message);
624
624
 
625
- // When a side-effect step is present and needs user approval, it's
626
- // shown in the main chat UI, regardless of if the walkthrough is
627
- // open or closed.
628
- // Once the user has approved/denied it, it goes back into the sidebar.
629
- // clang-format off
630
- const sideEffectStepsUI = sideEffectSteps.length > 0 ? sideEffectSteps.map(step => html`
631
- <div class="side-effect-container">
632
- ${renderStep({
633
- step,
634
- isLoading: input.isLoading,
635
- markdownRenderer: input.markdownRenderer,
636
- isLast: true
637
- })}
638
- </div> `) : Lit.nothing;
639
- // clang-format on
640
-
641
625
  // clang-format off
642
626
  const walkthroughInline = input.walkthrough.isInlined ? html`
643
627
  <div class="walkthrough-container">
@@ -656,7 +640,26 @@ function renderWalkthroughUI(input: ChatMessageViewInput, steps: Step[]): Lit.Li
656
640
  return html`
657
641
  ${openWalkThroughSidebarButton}
658
642
  ${walkthroughInline}
659
- ${sideEffectStepsUI}
643
+ `;
644
+ // clang-format on
645
+ }
646
+
647
+ function renderSideEffectStepsUI(input: ChatMessageViewInput, steps: Step[]): Lit.LitTemplate {
648
+ const sideEffectSteps = steps.filter(s => s.requestApproval);
649
+ if (sideEffectSteps.length === 0) {
650
+ return Lit.nothing;
651
+ }
652
+ // clang-format off
653
+ return html`
654
+ ${sideEffectSteps.map(step => html`
655
+ <div class="side-effect-container">
656
+ ${renderStep({
657
+ step,
658
+ isLoading: input.isLoading,
659
+ markdownRenderer: input.markdownRenderer,
660
+ isLast: true
661
+ })}
662
+ </div> `)}
660
663
  `;
661
664
  // clang-format on
662
665
  }
@@ -761,6 +764,15 @@ async function makeComputedStyleWidget(widgetData: ComputedStyleAiWidget): Promi
761
764
  }
762
765
  const styles = new ComputedStyle.ComputedStyleModel.ComputedStyle(domNodeForId, widgetData.data.computedStyles);
763
766
 
767
+ let filterText: RegExp|null = null;
768
+ try {
769
+ filterText = new RegExp(widgetData.data.properties.join('|'), 'i');
770
+ } catch {
771
+ // If the AI provides an invalid regex (e.g. "*"), we don't want to crash.
772
+ // We can just skip the widget in this case.
773
+ return null;
774
+ }
775
+
764
776
  // clang-format off
765
777
  const renderedWidget = html`<devtools-widget
766
778
  class="computed-styles-widget" ${widget(Elements.ComputedStyleWidget.ComputedStyleWidget, {
@@ -769,7 +781,7 @@ async function makeComputedStyleWidget(widgetData: ComputedStyleAiWidget): Promi
769
781
  // This disables showing the nested traces and detailed information in the widget.
770
782
  propertyTraces: null,
771
783
  allowUserControl: false,
772
- filterText: new RegExp(widgetData.data.properties.join('|'), 'i'),
784
+ filterText,
773
785
  enableNarrowViewResizing: false,
774
786
  })}></devtools-widget>`;
775
787
  // clang-format on
@@ -777,11 +789,19 @@ async function makeComputedStyleWidget(widgetData: ComputedStyleAiWidget): Promi
777
789
  return {
778
790
  renderedWidget,
779
791
  revealable: new Elements.ElementsPanel.NodeComputedStyles(domNodeForId),
780
- title: html`<devtools-widget
781
- ${widget(PanelsCommon.DOMLinkifier.DOMNodeLink, {
782
- node: domNodeForId,
783
- })}
784
- ></devtools-widget>`,
792
+ // clang-format off
793
+ title: html`
794
+ <span class="computed-style-title-wrapper">
795
+ <span class="computed-style-title-prefix">Computed styles</span>
796
+ <span class="style-class-wrapper">
797
+ (<devtools-widget
798
+ ${widget(PanelsCommon.DOMLinkifier.DOMNodeLink, {
799
+ node: domNodeForId,
800
+ })}
801
+ ></devtools-widget>)
802
+ </span>
803
+ </span>`,
804
+ // clang-format on
785
805
  };
786
806
  }
787
807
 
@@ -804,12 +824,21 @@ async function makeStylePropertiesWidget(widgetData: StylePropertiesAiWidget): P
804
824
  return null;
805
825
  }
806
826
 
827
+ let filter: RegExp|null = null;
828
+ try {
829
+ filter = widgetData.data.selector ? new RegExp(widgetData.data.selector) : null;
830
+ } catch {
831
+ // If the AI provides an invalid regex (e.g. "*"), we don't want to crash.
832
+ // We can just skip the widget in this case.
833
+ return null;
834
+ }
835
+
807
836
  // clang-format off
808
837
  const renderedWidget = html`<devtools-widget
809
838
  class="styling-preview-widget"
810
839
  ${widget(Elements.StandaloneStylesContainer.StandaloneStylesContainer, {
811
840
  domNode: domNodeForId,
812
- filter: widgetData.data.selector ? new RegExp(widgetData.data.selector) : null,
841
+ filter,
813
842
  })}>
814
843
  </devtools-widget>`;
815
844
  // clang-format on
@@ -1519,7 +1548,7 @@ async function makeTimelineRangeSummaryWidget(widgetData: TimelineRangeSummaryAi
1519
1548
  eventsArray.sort((a, b) => a.ts - b.ts);
1520
1549
 
1521
1550
  const thirdPartyTree = new Timeline.ThirdPartyTreeView.ThirdPartyTreeViewWidget();
1522
- const mapper = new Trace.EntityMapper.EntityMapper(parsedTrace);
1551
+ const mapper = Trace.EntityMapper.EntityMapper.getOrCreate(parsedTrace);
1523
1552
  thirdPartyTree.model = {selectedEvents: eventsArray, parsedTrace, entityMapper: mapper};
1524
1553
  thirdPartyTree.activeSelection = Timeline.TimelineSelection.selectionFromRangeMicroSeconds(bounds.min, bounds.max);
1525
1554
  thirdPartyTree.refreshTree(true);
@@ -1542,6 +1571,9 @@ async function makeTimelineRangeSummaryWidget(widgetData: TimelineRangeSummaryAi
1542
1571
  entityMapper: thirdPartyTree.entityMapper(),
1543
1572
  },
1544
1573
  activeSelection: { bounds },
1574
+ onBottomUpButtonClicked: (node: Trace.Extras.TraceTree.Node|null) => {
1575
+ void Common.Revealer.reveal(new TimelineUtils.Helpers.RevealableBottomUpProfile(bounds, node ?? undefined));
1576
+ },
1545
1577
  })}`,
1546
1578
  } as TimelineComponents.TimelineRangeSummaryView.TimelineRangeSummaryViewData,
1547
1579
  })}
@@ -19,7 +19,7 @@ const UIStrings = {
19
19
  /**
20
20
  * @description Title for the export for agents dialog.
21
21
  */
22
- exportForAgents: 'Copy for your coding agent',
22
+ exportForAgents: 'Copy to coding agent',
23
23
  /**
24
24
  * @description Button text for copying to clipboard.
25
25
  */
@@ -246,6 +246,7 @@ export class WalkthroughView extends UI.Widget.Widget {
246
246
  constructor(element?: HTMLElement, view: View = DEFAULT_VIEW) {
247
247
  super(element);
248
248
  this.#view = view;
249
+ this.setMinimumSize(330, 0);
249
250
  }
250
251
 
251
252
  override wasShown(): void {
@@ -13,6 +13,8 @@
13
13
  align-items: center;
14
14
  margin-block: calc(-1 * var(--sys-size-3));
15
15
  margin-top: var(--sys-size-5);
16
+ overflow: hidden;
17
+ mask-image: linear-gradient(to right, var(--ref-palette-neutral0) calc(100% - var(--sys-size-15)), transparent 100%);
16
18
 
17
19
  &.not-v2 {
18
20
  /* Can be removed when AIv2 ships */
@@ -456,6 +458,19 @@
456
458
  white-space: nowrap; /* stop the titles going onto multiple lines */
457
459
  }
458
460
 
461
+ /* This widget's title is some text + then a DOM node link, so it
462
+ * needs some extra styling */
463
+ .computed-style-title-wrapper {
464
+ display: flex;
465
+ align-items: center;
466
+ justify-content: flex-start;
467
+ gap: var(--sys-size-3);
468
+ }
469
+
470
+ .computed-style-title-prefix {
471
+ flex-shrink: 0;
472
+ }
473
+
459
474
  .widget-reveal-container {
460
475
  padding: 0;
461
476
  background: none;
@@ -99,23 +99,24 @@
99
99
 
100
100
  .inline-wrapper {
101
101
  display: flex;
102
- /* Note: no gap here; the gap is dealt with in padding on the text */
103
- align-items: flex-start;
102
+ align-items: center;
103
+ gap: var(--sys-size-2);
104
104
  justify-content: flex-start;
105
105
 
106
106
  .inline-icon {
107
107
  display: block;
108
- margin-top: var(--sys-size-2);
108
+ margin-top: var(--sys-size-2);
109
109
  }
110
110
  }
111
111
 
112
112
  .walkthrough-inline {
113
- border-radius: var(--sys-size-5);
113
+ border-radius: var(--sys-shape-corner-full);
114
114
  overflow: hidden;
115
115
  width: fit-content;
116
116
  max-width: 100%;
117
117
 
118
118
  &[open] {
119
+ border-radius: var(--sys-size-5);
119
120
  width: auto;
120
121
  background-color: var(--sys-color-surface1);
121
122
  margin-left: calc(var(--sys-size-6) / 2);
@@ -1031,6 +1031,14 @@ export class ApplicationPanelSidebar extends UI.Widget.VBox implements SDK.Targe
1031
1031
  }
1032
1032
  }
1033
1033
 
1034
+ showStorageBucket(bucketInfo: Protocol.Storage.StorageBucketInfo): void {
1035
+ const bucketsModel = SDK.TargetManager.TargetManager.instance().primaryPageTarget()?.model(
1036
+ SDK.StorageBucketsModel.StorageBucketsModel);
1037
+ if (bucketsModel) {
1038
+ this.storageBucketsTreeElement?.getBucketTreeElement(bucketsModel, bucketInfo)?.revealAndSelect(true);
1039
+ }
1040
+ }
1041
+
1034
1042
  private onmousemove(event: MouseEvent): void {
1035
1043
  const nodeUnderMouse = (event.target as Node);
1036
1044
  if (!nodeUnderMouse) {
@@ -147,7 +147,7 @@ export class IDBDatabaseView extends ApplicationComponents.StorageMetadataView.S
147
147
  super();
148
148
 
149
149
  this.model = model;
150
- this.setShowOnlyBucket(false);
150
+ this.setShowOnlyBucket(true);
151
151
  if (database) {
152
152
  this.update(database);
153
153
  }
@@ -12,6 +12,7 @@ import * as UI from '../../ui/legacy/legacy.js';
12
12
  import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';
13
13
 
14
14
  import {ApplicationPanelSidebar, StorageCategoryView} from './ApplicationPanelSidebar.js';
15
+ import type {StorageMetadataView} from './components/components.js';
15
16
  import {CookieItemsView} from './CookieItemsView.js';
16
17
  import type {DeviceBoundSessionsModel} from './DeviceBoundSessionsModel.js';
17
18
  import {DeviceBoundSessionsView} from './DeviceBoundSessionsView.js';
@@ -260,3 +261,10 @@ export class AttemptViewWithFilterRevealer implements
260
261
  sidebar.showPreloadingAttemptViewWithFilter(filter);
261
262
  }
262
263
  }
264
+
265
+ export class StorageBucketRevealer implements Common.Revealer.Revealer<StorageMetadataView.StorageBucketRevealInfo> {
266
+ async reveal(revealInfo: StorageMetadataView.StorageBucketRevealInfo): Promise<void> {
267
+ const sidebar = await ResourcesPanel.showAndGetSidebar();
268
+ sidebar.showStorageBucket(revealInfo.bucketInfo);
269
+ }
270
+ }
@@ -133,7 +133,7 @@ export class ServiceWorkerCacheView extends UI.View.SimpleView {
133
133
  .model(SDK.StorageBucketsModel.StorageBucketsModel)
134
134
  ?.getBucketByName(cache.storageBucket.storageKey, cache.storageBucket.name);
135
135
 
136
- this.metadataView.setShowOnlyBucket(false);
136
+ this.metadataView.setShowOnlyBucket(true);
137
137
 
138
138
  if (bucketInfo) {
139
139
  this.metadataView.setStorageBucket(bucketInfo);
@@ -174,3 +174,14 @@ Common.Revealer.registerRevealer({
174
174
  return new Resources.ResourcesPanel.AttemptViewWithFilterRevealer();
175
175
  },
176
176
  });
177
+
178
+ Common.Revealer.registerRevealer({
179
+ contextTypes() {
180
+ return maybeRetrieveContextTypes(Resources => [Resources.Components.StorageMetadataView.StorageBucketRevealInfo]);
181
+ },
182
+ destination: Common.Revealer.RevealerDestination.APPLICATION_PANEL,
183
+ async loadRevealer() {
184
+ const Resources = await loadResourcesModule();
185
+ return new Resources.ResourcesPanel.StorageBucketRevealer();
186
+ },
187
+ });
@@ -45,6 +45,7 @@ import * as TrustTokensTreeElement from './TrustTokensTreeElement.js';
45
45
  import * as WebMCPTreeElement from './WebMCPTreeElement.js';
46
46
  import * as WebMCPView from './WebMCPView.js';
47
47
 
48
+ export * as Components from './components/components.js';
48
49
  export {
49
50
  ApplicationPanelSidebar,
50
51
  AppManifestView,
@@ -4,7 +4,9 @@
4
4
  /* eslint-disable @devtools/no-lit-render-outside-of-view */
5
5
 
6
6
  import '../../../ui/components/report_view/report_view.js';
7
+ import '../../../ui/kit/kit.js';
7
8
 
9
+ import * as Common from '../../../core/common/common.js';
8
10
  import * as i18n from '../../../core/i18n/i18n.js';
9
11
  import * as SDK from '../../../core/sdk/sdk.js';
10
12
  import type * as Protocol from '../../../generated/protocol.js';
@@ -13,6 +15,7 @@ import * as LegacyWrapper from '../../../ui/components/legacy_wrapper/legacy_wra
13
15
  import * as RenderCoordinator from '../../../ui/components/render_coordinator/render_coordinator.js';
14
16
  import * as UI from '../../../ui/legacy/legacy.js';
15
17
  import {html, type LitTemplate, nothing, render, type TemplateResult} from '../../../ui/lit/lit.js';
18
+ import * as VisualLogging from '../../../ui/visual_logging/visual_logging.js';
16
19
 
17
20
  import storageMetadataViewStyle from './storageMetadataView.css.js';
18
21
 
@@ -113,12 +116,17 @@ const UIStrings = {
113
116
  const str_ = i18n.i18n.registerUIStrings('panels/application/components/StorageMetadataView.ts', UIStrings);
114
117
  const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
115
118
 
119
+ export class StorageBucketRevealInfo {
120
+ constructor(public bucketInfo: Protocol.Storage.StorageBucketInfo) {
121
+ }
122
+ }
123
+
116
124
  export class StorageMetadataView extends LegacyWrapper.LegacyWrapper.WrappableComponent {
117
125
  readonly #shadow = this.attachShadow({mode: 'open'});
118
126
  #storageBucketsModel?: SDK.StorageBucketsModel.StorageBucketsModel;
119
127
  #storageKey: SDK.StorageKeyManager.StorageKey|null = null;
120
128
  #storageBucket: Protocol.Storage.StorageBucketInfo|null = null;
121
- #showOnlyBucket = true;
129
+ #showOnlyBucket = false;
122
130
 
123
131
  setStorageKey(storageKey: string): void {
124
132
  this.#storageKey = SDK.StorageKeyManager.parseStorageKey(storageKey);
@@ -220,20 +228,36 @@ export class StorageMetadataView extends LegacyWrapper.LegacyWrapper.WrappableCo
220
228
  const {bucket: {name}, persistent, durability, quota} = this.#storageBucket;
221
229
  const isDefault = !name;
222
230
 
223
- if (!this.#showOnlyBucket) {
231
+ const renderBucketName = (): TemplateResult => {
224
232
  if (isDefault) {
225
- return html`
226
- ${this.key(i18nString(UIStrings.bucketName))}
227
- ${this.value(html`<span class="default-bucket">default</span>`)}`;
233
+ return html`<span class="default-bucket">${i18nString(UIStrings.defaultBucket)}</span>`;
228
234
  }
235
+ if (!this.#showOnlyBucket) {
236
+ return html`${name}`;
237
+ }
238
+ const revealBucket = (e: Event): void => {
239
+ e.preventDefault();
240
+ void Common.Revealer.reveal(
241
+ new StorageBucketRevealInfo(this.#storageBucket as Protocol.Storage.StorageBucketInfo));
242
+ };
243
+ return html`<devtools-link
244
+ @click=${revealBucket}
245
+ title=${name}
246
+ jslog=${VisualLogging.action('storage-bucket').track({
247
+ click: true
248
+ })}
249
+ >${name}</devtools-link>`;
250
+ };
251
+
252
+ if (this.#showOnlyBucket) {
229
253
  return html`
230
254
  ${this.key(i18nString(UIStrings.bucketName))}
231
- ${this.value(name)}`;
255
+ ${this.value(renderBucketName())}`;
232
256
  }
233
257
  // clang-format off
234
258
  return html`
235
259
  ${this.key(i18nString(UIStrings.bucketName))}
236
- ${this.value(name || html`<span class="default-bucket">default</span>`)}
260
+ ${this.value(renderBucketName())}
237
261
  ${this.key(i18nString(UIStrings.persistent))}
238
262
  ${this.value(persistent ? i18nString(UIStrings.yes) : i18nString(UIStrings.no))}
239
263
  ${this.key(i18nString(UIStrings.durability))}
@@ -4,6 +4,10 @@
4
4
  * found in the LICENSE file.
5
5
  */
6
6
 
7
+ :host {
8
+ overflow: auto;
9
+ }
10
+
7
11
  devtools-report-value {
8
12
  overflow: hidden;
9
13
  }
@@ -54,6 +54,10 @@ const UIStrings = {
54
54
  * @description Text in details
55
55
  */
56
56
  detailsTargetHint: 'Target hint',
57
+ /**
58
+ * @description Text in details
59
+ */
60
+ detailsFormSubmission: 'Form submission',
57
61
  /**
58
62
  * @description Text in details
59
63
  */
@@ -62,6 +66,14 @@ const UIStrings = {
62
66
  * @description Header of rule set
63
67
  */
64
68
  detailsRuleSet: 'Rule set',
69
+ /**
70
+ * @description Text indicating that the preloading field is true.
71
+ */
72
+ yes: 'Yes',
73
+ /**
74
+ * @description Text indicating that the preloading field is false.
75
+ */
76
+ no: 'No',
65
77
  /**
66
78
  * @description Description: status
67
79
  */
@@ -202,6 +214,7 @@ export class PreloadingDetailsReportView extends LegacyWrapper.LegacyWrapper.Wra
202
214
  ${this.#action(isFallbackToPrefetch)}
203
215
  ${this.#status(isFallbackToPrefetch)}
204
216
  ${this.#targetHint()}
217
+ ${this.#formSubmission()}
205
218
  ${this.#maybePrefetchFailureReason()}
206
219
  ${this.#maybePrerenderFailureReason()}
207
220
 
@@ -381,6 +394,22 @@ export class PreloadingDetailsReportView extends LegacyWrapper.LegacyWrapper.Wra
381
394
  `;
382
395
  }
383
396
 
397
+ #formSubmission(): Lit.LitTemplate {
398
+ assertNotNullOrUndefined(this.#data);
399
+ const attempt = this.#data.pipeline.getOriginallyTriggered();
400
+ const hasFormSubmission = attempt.key.formSubmission !== undefined;
401
+ if (!hasFormSubmission || !this.#isPrerenderLike(attempt.action)) {
402
+ return Lit.nothing;
403
+ }
404
+
405
+ return html`
406
+ <devtools-report-key>${i18nString(UIStrings.detailsFormSubmission)}</devtools-report-key>
407
+ <devtools-report-value>
408
+ ${attempt.key.formSubmission ? i18nString(UIStrings.yes) : i18nString(UIStrings.no)}
409
+ </devtools-report-value>
410
+ `;
411
+ }
412
+
384
413
  #maybePrerenderFailureReason(): Lit.LitTemplate {
385
414
  assertNotNullOrUndefined(this.#data);
386
415
  const attempt = this.#data.pipeline.getOriginallyTriggered();
@@ -2850,6 +2850,10 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
2850
2850
  }
2851
2851
 
2852
2852
  updateDecorations(): void {
2853
+ // Important to keep the entire tree node row as a clickable area for that
2854
+ // node.
2855
+ this.listItemElement.style.setProperty('--indent', this.computeLeftIndent() + 'px');
2856
+
2853
2857
  if (this.isClosingTag()) {
2854
2858
  return;
2855
2859
  }
@@ -159,7 +159,6 @@ export class LighthousePanel extends UI.Panel.Panel {
159
159
  this.renderStatusView();
160
160
  const {lhr, artifacts} = await this.controller.collectLighthouseResults();
161
161
  this.buildReportUI(lhr, artifacts);
162
- UI.Context.Context.instance().setFlavor(ActiveLighthouseReport, new ActiveLighthouseReport(lhr));
163
162
  return {report: lhr};
164
163
  } catch (err) {
165
164
  this.handleError(err);
@@ -278,6 +277,7 @@ export class LighthousePanel extends UI.Panel.Panel {
278
277
  this.statusView.hide();
279
278
 
280
279
  this.reportSelector.selectNewReport();
280
+ UI.Context.Context.instance().setFlavor(ActiveLighthouseReport, null);
281
281
  this.contentElement.classList.toggle('in-progress', false);
282
282
 
283
283
  this.startView.show(this.contentElement);
@@ -324,6 +324,8 @@ export class LighthousePanel extends UI.Panel.Panel {
324
324
  this.newButton.setEnabled(true);
325
325
  this.refreshToolbarUI();
326
326
 
327
+ UI.Context.Context.instance().setFlavor(ActiveLighthouseReport, new ActiveLighthouseReport(lighthouseResult));
328
+
327
329
  const cachedRenderedReport = this.cachedRenderedReports.get(lighthouseResult);
328
330
  if (cachedRenderedReport) {
329
331
  this.auditResultsElement.appendChild(cachedRenderedReport);
@@ -5,7 +5,7 @@
5
5
  import * as Common from '../../core/common/common.js';
6
6
  import * as i18n from '../../core/i18n/i18n.js';
7
7
  import type * as SDK from '../../core/sdk/sdk.js';
8
- import * as HeapSnapshotModel from '../../models/heap_snapshot_model/heap_snapshot_model.js';
8
+ import * as HeapSnapshotModel from '../../models/heap_snapshot/heap_snapshot.js';
9
9
  import * as DataGrid from '../../ui/legacy/components/data_grid/data_grid.js';
10
10
  import * as Components from '../../ui/legacy/components/utils/utils.js';
11
11
  import * as UI from '../../ui/legacy/legacy.js';
@@ -10,7 +10,7 @@ import * as i18n from '../../core/i18n/i18n.js';
10
10
  import * as Platform from '../../core/platform/platform.js';
11
11
  import * as SDK from '../../core/sdk/sdk.js';
12
12
  import type * as Protocol from '../../generated/protocol.js';
13
- import * as HeapSnapshotModel from '../../models/heap_snapshot_model/heap_snapshot_model.js';
13
+ import * as HeapSnapshotModel from '../../models/heap_snapshot/heap_snapshot.js';
14
14
  import {createIcon} from '../../ui/kit/kit.js';
15
15
  import * as DataGrid from '../../ui/legacy/components/data_grid/data_grid.js';
16
16
  import * as UI from '../../ui/legacy/legacy.js';
@@ -11,7 +11,7 @@ import * as Platform from '../../core/platform/platform.js';
11
11
  import * as SDK from '../../core/sdk/sdk.js';
12
12
  import type * as Protocol from '../../generated/protocol.js';
13
13
  import * as Bindings from '../../models/bindings/bindings.js';
14
- import * as HeapSnapshotModel from '../../models/heap_snapshot_model/heap_snapshot_model.js';
14
+ import * as HeapSnapshotModel from '../../models/heap_snapshot/heap_snapshot.js';
15
15
  import * as DataGrid from '../../ui/legacy/components/data_grid/data_grid.js';
16
16
  import * as ObjectUI from '../../ui/legacy/components/object_ui/object_ui.js';
17
17
  import * as PerfUI from '../../ui/legacy/components/perf_ui/perf_ui.js';