chrome-devtools-frontend 1.0.1558690 → 1.0.1559913

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 (51) hide show
  1. package/front_end/Images/src/container.svg +4 -0
  2. package/front_end/core/common/Gzip.ts +15 -0
  3. package/front_end/core/sdk/CSSMetadata.ts +6 -6
  4. package/front_end/core/sdk/CSSModel.ts +2 -2
  5. package/front_end/core/sdk/DOMModel.ts +7 -3
  6. package/front_end/generated/InspectorBackendCommands.ts +2 -1
  7. package/front_end/generated/SupportedCSSProperties.js +64 -32
  8. package/front_end/generated/protocol-mapping.d.ts +9 -0
  9. package/front_end/generated/protocol-proxy-api.d.ts +7 -0
  10. package/front_end/generated/protocol.ts +14 -1
  11. package/front_end/models/ai_assistance/agents/StylingAgent.ts +1 -1
  12. package/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.ts +11 -7
  13. package/front_end/models/trace/LanternComputationData.ts +4 -3
  14. package/front_end/models/trace/Processor.ts +6 -5
  15. package/front_end/models/trace/Styles.ts +10 -1
  16. package/front_end/models/trace/handlers/LargestImagePaintHandler.ts +2 -2
  17. package/front_end/models/trace/handlers/MetaHandler.ts +14 -0
  18. package/front_end/models/trace/handlers/PageLoadMetricsHandler.ts +54 -34
  19. package/front_end/models/trace/helpers/Timing.ts +8 -1
  20. package/front_end/models/trace/insights/Common.ts +1 -1
  21. package/front_end/models/trace/insights/LCPBreakdown.ts +4 -4
  22. package/front_end/models/trace/insights/LCPDiscovery.ts +3 -3
  23. package/front_end/models/trace/insights/RenderBlocking.ts +1 -1
  24. package/front_end/models/trace/insights/types.ts +1 -1
  25. package/front_end/models/trace/types/TraceEvents.ts +62 -10
  26. package/front_end/panels/common/AiCodeGenerationTeaser.ts +48 -12
  27. package/front_end/panels/common/aiCodeGenerationTeaser.css +14 -0
  28. package/front_end/panels/common/common.ts +1 -1
  29. package/front_end/panels/console/consoleView.css +1 -1
  30. package/front_end/panels/elements/CSSRuleValidator.ts +38 -0
  31. package/front_end/panels/elements/ElementsTreeElement.ts +79 -58
  32. package/front_end/panels/elements/ElementsTreeOutline.ts +0 -17
  33. package/front_end/panels/elements/StylesSidebarPane.ts +15 -4
  34. package/front_end/panels/timeline/StatusDialog.ts +4 -3
  35. package/front_end/panels/timeline/TimelineFlameChartNetworkDataProvider.ts +3 -16
  36. package/front_end/panels/timeline/TimelineFlameChartView.ts +64 -21
  37. package/front_end/panels/timeline/TimelinePanel.ts +71 -24
  38. package/front_end/panels/timeline/TimelineUIUtils.ts +28 -2
  39. package/front_end/panels/timeline/TimingsTrackAppender.ts +3 -1
  40. package/front_end/panels/timeline/components/SidebarSingleInsightSet.ts +1 -1
  41. package/front_end/panels/timeline/components/insights/RenderBlocking.ts +6 -4
  42. package/front_end/panels/timeline/overlays/OverlaysImpl.ts +4 -0
  43. package/front_end/panels/timeline/timelinePanel.css +8 -1
  44. package/front_end/panels/timeline/utils/EntryNodes.ts +2 -1
  45. package/front_end/third_party/chromium/README.chromium +1 -1
  46. package/front_end/ui/components/text_editor/AiCodeGenerationProvider.ts +70 -28
  47. package/front_end/ui/legacy/SearchableView.ts +11 -5
  48. package/front_end/ui/legacy/SplitWidget.ts +1 -1
  49. package/front_end/ui/legacy/components/perf_ui/FlameChart.ts +43 -9
  50. package/front_end/ui/visual_logging/KnownContextValues.ts +13 -0
  51. package/package.json +1 -1
@@ -0,0 +1,14 @@
1
+ /*
2
+ * Copyright 2025 The Chromium Authors
3
+ * Use of this source code is governed by a BSD-style license that can be
4
+ * found in the LICENSE file.
5
+ */
6
+
7
+ @scope to (devtools-widget > *) {
8
+ .ai-code-generation-teaser {
9
+ .new-badge {
10
+ font-style: normal;
11
+ display: inline-block;
12
+ }
13
+ }
14
+ }
@@ -95,7 +95,7 @@ export class TypeToAllowDialog {
95
95
  }
96
96
 
97
97
  export {AiCodeCompletionTeaser} from './AiCodeCompletionTeaser.js';
98
- export {AiCodeGenerationTeaser} from './AiCodeGenerationTeaser.js';
98
+ export * as AiCodeGenerationTeaser from './AiCodeGenerationTeaser.js';
99
99
  export {AnnotationManager} from './AnnotationManager.js';
100
100
  export {FreDialog} from './FreDialog.js';
101
101
  export {GdpSignUpDialog} from './GdpSignUpDialog.js';
@@ -221,7 +221,7 @@
221
221
  --console-color-white: #fff;
222
222
 
223
223
  &:focus {
224
- background-color: var(--sys-color-state-focus-highlight);
224
+ background-image: linear-gradient(to bottom, var(--sys-color-state-focus-highlight), var(--sys-color-state-focus-highlight));
225
225
  }
226
226
  }
227
227
 
@@ -85,6 +85,20 @@ const UIStrings = {
85
85
  */
86
86
  flexGridContainerPropertyRuleFix:
87
87
  'Try setting the {PROPERTY_NAME} on the container element or use {ALTERNATIVE_PROPERTY_NAME} instead.',
88
+ /**
89
+ * @description The messages shown in the Style pane when the user hovers over a position-anchor declaration that has no affect on a non-anchor-positioned element.
90
+ * @example {relative} POSITION
91
+ */
92
+ invalidAnchorPositioning:
93
+ 'An anchor was defined but the element was not anchor-positioned but positioned "{POSITION}".',
94
+ /**
95
+ * @description The messages shown in the Style pane when the user hovers over a position-anchor declaration that has no affect on a non-anchor-positioned element.
96
+ */
97
+ invalidAnchorPositioningFix: 'Set position to either "fixed" or "absolute".',
98
+ /**
99
+ * @description The messages shown in the Style pane when the user hovers over a position-anchor declaration that has no affect on hidden element.
100
+ */
101
+ unusedAnchorPositioning: 'An anchor was defined but the element is hidden.',
88
102
  } as const;
89
103
  const str_ = i18n.i18n.registerUIStrings('panels/elements/CSSRuleValidator.ts', UIStrings);
90
104
  const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
@@ -547,6 +561,29 @@ export class ZIndexValidator extends CSSRuleValidator {
547
561
  }
548
562
  }
549
563
 
564
+ export class PositionAnchorValidator extends CSSRuleValidator {
565
+ constructor() {
566
+ super(['position-anchor']);
567
+ }
568
+
569
+ override getHint(propertyName: string, computedStyles?: Map<string, string>): Hint|undefined {
570
+ const position = computedStyles?.get('position') ?? 'static';
571
+ const display = computedStyles?.get('display');
572
+
573
+ if (position !== 'absolute' && position !== 'fixed') {
574
+ return new Hint(
575
+ i18nString(UIStrings.invalidAnchorPositioning, {POSITION: position}),
576
+ i18nString(UIStrings.invalidAnchorPositioningFix));
577
+ }
578
+
579
+ if (display === 'none') {
580
+ return new Hint(i18nString(UIStrings.unusedAnchorPositioning, {POSITION: position}), null);
581
+ }
582
+
583
+ return undefined;
584
+ }
585
+ }
586
+
550
587
  /**
551
588
  * Validates if CSS width/height are having an effect on an element.
552
589
  * See "Applies to" in https://www.w3.org/TR/css-sizing-3/#propdef-width.
@@ -659,6 +696,7 @@ const CSS_RULE_VALIDATORS = [
659
696
  MulticolFlexGridValidator,
660
697
  PaddingValidator,
661
698
  PositionValidator,
699
+ PositionAnchorValidator,
662
700
  SizingValidator,
663
701
  ZIndexValidator,
664
702
  ];
@@ -383,6 +383,7 @@ export interface ViewInput {
383
383
 
384
384
  showAdAdorner: boolean;
385
385
  showContainerAdorner: boolean;
386
+ containerType?: string;
386
387
  showFlexAdorner: boolean;
387
388
  showGridAdorner: boolean;
388
389
  showGridLanesAdorner: boolean;
@@ -392,6 +393,7 @@ export interface ViewInput {
392
393
  isSubgrid: boolean;
393
394
 
394
395
  showViewSourceAdorner: boolean;
396
+ showScrollAdorner: boolean;
395
397
  adorners?: Set<Adorners.Adorner.Adorner>;
396
398
  nodeInfo?: DocumentFragment;
397
399
  topLayerIndex: number;
@@ -454,9 +456,11 @@ export const DEFAULT_VIEW = (input: ViewInput, output: ViewOutput, target: HTMLE
454
456
  ElementsComponents.AdornerManager.RegisteredAdorners.POPOVER);
455
457
  const topLayerAdornerConfig = ElementsComponents.AdornerManager.getRegisteredAdorner(
456
458
  ElementsComponents.AdornerManager.RegisteredAdorners.TOP_LAYER);
459
+ const scrollAdornerConfig = ElementsComponents.AdornerManager.getRegisteredAdorner(
460
+ ElementsComponents.AdornerManager.RegisteredAdorners.SCROLL);
457
461
  const hasAdorners = input.adorners?.size || input.showAdAdorner || input.showContainerAdorner ||
458
462
  input.showFlexAdorner || input.showGridAdorner || input.showGridLanesAdorner || input.showMediaAdorner ||
459
- input.showPopoverAdorner || input.showTopLayerAdorner || input.showViewSourceAdorner;
463
+ input.showPopoverAdorner || input.showTopLayerAdorner || input.showViewSourceAdorner || input.showScrollAdorner;
460
464
  // clang-format off
461
465
  render(html`
462
466
  <div ${ref(el => { output.contentElement = el as HTMLElement; })}>
@@ -496,7 +500,10 @@ export const DEFAULT_VIEW = (input: ViewInput, output: ViewOutput, target: HTMLE
496
500
  }
497
501
  }}
498
502
  ${adornerRef(input)}>
499
- <span>${containerAdornerConfig.name}</span>
503
+ <span class="adorner-with-icon">
504
+ <devtools-icon name="container"></devtools-icon>
505
+ <span>${input.containerType}</span>
506
+ </span>
500
507
  </devtools-adorner>`: nothing}
501
508
  ${input.showFlexAdorner ? html`<devtools-adorner
502
509
  class=clickable
@@ -618,6 +625,13 @@ export const DEFAULT_VIEW = (input: ViewInput, output: ViewOutput, target: HTMLE
618
625
  ${repeat(Array.from((input.adorners ?? new Set()).values()).sort(adornerComparator), adorner => {
619
626
  return adorner;
620
627
  })}
628
+ ${input.showScrollAdorner ? html`<devtools-adorner
629
+ class="scroll"
630
+ .data=${{name: scrollAdornerConfig.name, jslogContext: scrollAdornerConfig.name}}
631
+ aria-label=${i18nString(UIStrings.elementHasScrollableOverflow)}
632
+ ${adornerRef(input)}>
633
+ <span>${scrollAdornerConfig.name}</span>
634
+ </devtools-adorner>` : nothing}
621
635
  </div>`: nothing}
622
636
  </div>
623
637
  `, target);
@@ -689,8 +703,6 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
689
703
  };
690
704
  void this.updateStyleAdorners();
691
705
 
692
- void this.updateScrollAdorner();
693
-
694
706
  void this.#updateAdorners();
695
707
  }
696
708
  this.expandAllButtonElement = null;
@@ -711,34 +723,6 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
711
723
  if (this.nodeInternal.detached && !this.isClosingTag()) {
712
724
  this.listItemNode.setAttribute('title', 'Detached Tree Node');
713
725
  }
714
-
715
- node.domModel().overlayModel().addEventListener(
716
- SDK.OverlayModel.Events.PERSISTENT_CONTAINER_QUERY_OVERLAY_STATE_CHANGED, event => {
717
- const {nodeId: eventNodeId, enabled} = event.data;
718
- if (eventNodeId !== node.id) {
719
- return;
720
- }
721
- this.#containerAdornerActive = enabled;
722
- this.performUpdate();
723
- });
724
- node.domModel().overlayModel().addEventListener(
725
- SDK.OverlayModel.Events.PERSISTENT_FLEX_CONTAINER_OVERLAY_STATE_CHANGED, event => {
726
- const {nodeId: eventNodeId, enabled} = event.data;
727
- if (eventNodeId !== node.id) {
728
- return;
729
- }
730
- this.#flexAdornerActive = enabled;
731
- this.performUpdate();
732
- });
733
- node.domModel().overlayModel().addEventListener(
734
- SDK.OverlayModel.Events.PERSISTENT_GRID_OVERLAY_STATE_CHANGED, event => {
735
- const {nodeId: eventNodeId, enabled} = event.data;
736
- if (eventNodeId !== node.id) {
737
- return;
738
- }
739
- this.#gridAdornerActive = enabled;
740
- this.performUpdate();
741
- });
742
726
  }
743
727
 
744
728
  static animateOnDOMUpdate(treeElement: ElementsTreeElement): void {
@@ -804,7 +788,8 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
804
788
  containerAdornerActive: this.#containerAdornerActive,
805
789
  adorners: !this.isClosingTag() ? this.#adorners : undefined,
806
790
  showAdAdorner: this.nodeInternal.isAdFrameNode(),
807
- showContainerAdorner: Boolean(this.#layout?.isContainer) && !this.isClosingTag(),
791
+ showContainerAdorner: Boolean(this.#layout?.containerType) && !this.isClosingTag(),
792
+ containerType: this.#layout?.containerType,
808
793
  showFlexAdorner: Boolean(this.#layout?.isFlex) && !this.isClosingTag(),
809
794
  flexAdornerActive: this.#flexAdornerActive,
810
795
  showGridAdorner: Boolean(this.#layout?.isGrid) && !this.isClosingTag(),
@@ -817,6 +802,9 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
817
802
  popoverAdornerActive: this.#popoverAdornerActive,
818
803
  isSubgrid: Boolean(this.#layout?.isSubgrid),
819
804
  showViewSourceAdorner: this.nodeInternal.isRootNode() && isOpeningTag(this.tagTypeContext),
805
+ showScrollAdorner: ((this.node().nodeName() === 'HTML' && this.node().ownerDocument?.isScrollable()) ||
806
+ (this.node().nodeName() !== '#document' && this.node().isScrollable())) &&
807
+ !this.isClosingTag(),
820
808
  nodeInfo: this.#nodeInfo,
821
809
  topLayerIndex: this.node().topLayerIndex(),
822
810
  onViewSourceAdornerClick: this.revealHTMLInSources.bind(this),
@@ -1124,6 +1112,18 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
1124
1112
  if (this.treeOutline && !this.isClosingTag()) {
1125
1113
  this.treeOutline.treeElementByNode.set(this.nodeInternal, this);
1126
1114
  this.nodeInternal.addEventListener(SDK.DOMModel.DOMNodeEvents.TOP_LAYER_INDEX_CHANGED, this.performUpdate, this);
1115
+ this.nodeInternal.addEventListener(
1116
+ SDK.DOMModel.DOMNodeEvents.SCROLLABLE_FLAG_UPDATED, this.#onScrollableFlagUpdated, this);
1117
+ const overlayModel = this.nodeInternal.domModel().overlayModel();
1118
+ overlayModel.addEventListener(
1119
+ SDK.OverlayModel.Events.PERSISTENT_CONTAINER_QUERY_OVERLAY_STATE_CHANGED,
1120
+ this.#onPersistentContainerQueryOverlayStateChanged, this);
1121
+ overlayModel.addEventListener(
1122
+ SDK.OverlayModel.Events.PERSISTENT_FLEX_CONTAINER_OVERLAY_STATE_CHANGED,
1123
+ this.#onPersistentFlexContainerOverlayStateChanged, this);
1124
+ overlayModel.addEventListener(
1125
+ SDK.OverlayModel.Events.PERSISTENT_GRID_OVERLAY_STATE_CHANGED, this.#onPersistentGridOverlayStateChanged,
1126
+ this);
1127
1127
  }
1128
1128
  }
1129
1129
 
@@ -1135,6 +1135,51 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
1135
1135
  this.treeOutline.treeElementByNode.delete(this.nodeInternal);
1136
1136
  }
1137
1137
  this.nodeInternal.removeEventListener(SDK.DOMModel.DOMNodeEvents.TOP_LAYER_INDEX_CHANGED, this.performUpdate, this);
1138
+ this.nodeInternal.removeEventListener(
1139
+ SDK.DOMModel.DOMNodeEvents.SCROLLABLE_FLAG_UPDATED, this.#onScrollableFlagUpdated, this);
1140
+ const overlayModel = this.nodeInternal.domModel().overlayModel();
1141
+ overlayModel.removeEventListener(
1142
+ SDK.OverlayModel.Events.PERSISTENT_CONTAINER_QUERY_OVERLAY_STATE_CHANGED,
1143
+ this.#onPersistentContainerQueryOverlayStateChanged, this);
1144
+ overlayModel.removeEventListener(
1145
+ SDK.OverlayModel.Events.PERSISTENT_FLEX_CONTAINER_OVERLAY_STATE_CHANGED,
1146
+ this.#onPersistentFlexContainerOverlayStateChanged, this);
1147
+ overlayModel.removeEventListener(
1148
+ SDK.OverlayModel.Events.PERSISTENT_GRID_OVERLAY_STATE_CHANGED, this.#onPersistentGridOverlayStateChanged, this);
1149
+ }
1150
+
1151
+ #onScrollableFlagUpdated(): void {
1152
+ void this.#updateAdorners();
1153
+ }
1154
+
1155
+ #onPersistentContainerQueryOverlayStateChanged(
1156
+ event: Common.EventTarget.EventTargetEvent<SDK.OverlayModel.ChangedNodeId>): void {
1157
+ const {nodeId: eventNodeId, enabled} = event.data;
1158
+ if (eventNodeId !== this.nodeInternal.id) {
1159
+ return;
1160
+ }
1161
+ this.#containerAdornerActive = enabled;
1162
+ this.performUpdate();
1163
+ }
1164
+
1165
+ #onPersistentFlexContainerOverlayStateChanged(
1166
+ event: Common.EventTarget.EventTargetEvent<SDK.OverlayModel.ChangedNodeId>): void {
1167
+ const {nodeId: eventNodeId, enabled} = event.data;
1168
+ if (eventNodeId !== this.nodeInternal.id) {
1169
+ return;
1170
+ }
1171
+ this.#flexAdornerActive = enabled;
1172
+ this.performUpdate();
1173
+ }
1174
+
1175
+ #onPersistentGridOverlayStateChanged(event: Common.EventTarget.EventTargetEvent<SDK.OverlayModel.ChangedNodeId>):
1176
+ void {
1177
+ const {nodeId: eventNodeId, enabled} = event.data;
1178
+ if (eventNodeId !== this.nodeInternal.id) {
1179
+ return;
1180
+ }
1181
+ this.#gridAdornerActive = enabled;
1182
+ this.performUpdate();
1138
1183
  }
1139
1184
 
1140
1185
  override onattach(): void {
@@ -1492,7 +1537,7 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
1492
1537
  ],
1493
1538
  },
1494
1539
  {
1495
- condition: (props: SDK.CSSModel.LayoutProperties|null): boolean => Boolean(props?.isContainer),
1540
+ condition: (props: SDK.CSSModel.LayoutProperties|null): boolean => Boolean(props?.containerType),
1496
1541
  items: [
1497
1542
  {
1498
1543
  label: i18nString(UIStrings.explainContainerQueries),
@@ -3058,30 +3103,6 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
3058
3103
  this.#adorners.add(adorner);
3059
3104
  }
3060
3105
 
3061
- updateScrollAdorner(): void {
3062
- if (!isOpeningTag(this.tagTypeContext)) {
3063
- return;
3064
- }
3065
- const scrollAdorner =
3066
- this.#adorners.values().find(x => x.name === ElementsComponents.AdornerManager.RegisteredAdorners.SCROLL);
3067
- // Check if the node is scrollable, or if it's the <html> element and the document is scrollable
3068
- // because the top-level document (#document) doesn't have a corresponding tree element.
3069
- const needsAScrollAdorner = (this.node().nodeName() === 'HTML' && this.node().ownerDocument?.isScrollable()) ||
3070
- (this.node().nodeName() !== '#document' && this.node().isScrollable());
3071
- if (needsAScrollAdorner && !scrollAdorner) {
3072
- this.pushScrollAdorner();
3073
- } else if (!needsAScrollAdorner && scrollAdorner) {
3074
- this.removeAdorner(scrollAdorner);
3075
- }
3076
- }
3077
-
3078
- pushScrollAdorner(): void {
3079
- const config = ElementsComponents.AdornerManager.getRegisteredAdorner(
3080
- ElementsComponents.AdornerManager.RegisteredAdorners.SCROLL);
3081
- const adorner = this.adorn(config);
3082
- UI.Tooltip.Tooltip.install(adorner, i18nString(UIStrings.elementHasScrollableOverflow));
3083
- adorner.classList.add('scroll');
3084
- }
3085
3106
  }
3086
3107
 
3087
3108
  export const InitialChildrenLimit = 500;
@@ -1526,7 +1526,6 @@ export class ElementsTreeOutline extends
1526
1526
  domModel.addEventListener(SDK.DOMModel.Events.DocumentUpdated, this.documentUpdated, this);
1527
1527
  domModel.addEventListener(SDK.DOMModel.Events.ChildNodeCountUpdated, this.childNodeCountUpdated, this);
1528
1528
  domModel.addEventListener(SDK.DOMModel.Events.DistributedNodesChanged, this.distributedNodesChanged, this);
1529
- domModel.addEventListener(SDK.DOMModel.Events.ScrollableFlagUpdated, this.scrollableFlagUpdated, this);
1530
1529
  domModel.addEventListener(
1531
1530
  SDK.DOMModel.Events.AffectedByStartingStylesFlagUpdated, this.affectedByStartingStylesFlagUpdated, this);
1532
1531
  domModel.addEventListener(SDK.DOMModel.Events.AdoptedStyleSheetsModified, this.adoptedStyleSheetsModified, this);
@@ -1542,7 +1541,6 @@ export class ElementsTreeOutline extends
1542
1541
  domModel.removeEventListener(SDK.DOMModel.Events.DocumentUpdated, this.documentUpdated, this);
1543
1542
  domModel.removeEventListener(SDK.DOMModel.Events.ChildNodeCountUpdated, this.childNodeCountUpdated, this);
1544
1543
  domModel.removeEventListener(SDK.DOMModel.Events.DistributedNodesChanged, this.distributedNodesChanged, this);
1545
- domModel.removeEventListener(SDK.DOMModel.Events.ScrollableFlagUpdated, this.scrollableFlagUpdated, this);
1546
1544
  domModel.removeEventListener(
1547
1545
  SDK.DOMModel.Events.AffectedByStartingStylesFlagUpdated, this.affectedByStartingStylesFlagUpdated, this);
1548
1546
  domModel.removeEventListener(SDK.DOMModel.Events.AdoptedStyleSheetsModified, this.adoptedStyleSheetsModified, this);
@@ -2020,21 +2018,6 @@ export class ElementsTreeOutline extends
2020
2018
  }
2021
2019
  }
2022
2020
 
2023
- private scrollableFlagUpdated(event: Common.EventTarget.EventTargetEvent<{node: SDK.DOMModel.DOMNode}>): void {
2024
- let {node} = event.data;
2025
- if (node.nodeName() === '#document') {
2026
- // We show the scroll badge of the document on the <html> element.
2027
- if (!node.ownerDocument?.documentElement) {
2028
- return;
2029
- }
2030
- node = node.ownerDocument.documentElement;
2031
- }
2032
- const treeElement = this.treeElementByNode.get(node);
2033
- if (treeElement && isOpeningTag(treeElement.tagTypeContext)) {
2034
- void treeElement.updateScrollAdorner();
2035
- }
2036
- }
2037
-
2038
2021
  private affectedByStartingStylesFlagUpdated(event: Common.EventTarget.EventTargetEvent<{node: SDK.DOMModel.DOMNode}>):
2039
2022
  void {
2040
2023
  const {node} = event.data;
@@ -482,6 +482,10 @@ export class StylesSidebarPane extends Common.ObjectWrapper.eventMixin<EventType
482
482
 
483
483
  private onFilterChanged(event: Common.EventTarget.EventTargetEvent<string>): void {
484
484
  const regex = event.data ? new RegExp(Platform.StringUtilities.escapeForRegExp(event.data), 'i') : null;
485
+ this.setFilter(regex);
486
+ }
487
+
488
+ setFilter(regex: RegExp|null): void {
485
489
  this.lastFilterChange = Date.now();
486
490
  this.#filterRegex = regex;
487
491
  this.updateFilter();
@@ -1036,6 +1040,7 @@ export class StylesSidebarPane extends Common.ObjectWrapper.eventMixin<EventType
1036
1040
  let sectionIdx = 0;
1037
1041
  let lastParentNode: SDK.DOMModel.DOMNode|null = null;
1038
1042
 
1043
+ let lastLayerParent: SectionBlock|undefined;
1039
1044
  let lastLayers: SDK.CSSLayer.CSSLayer[]|null = null;
1040
1045
  let sawLayers = false;
1041
1046
 
@@ -1046,6 +1051,7 @@ export class StylesSidebarPane extends Common.ObjectWrapper.eventMixin<EventType
1046
1051
  if ((layers.length || lastLayers) && lastLayers !== layers) {
1047
1052
  const block = SectionBlock.createLayerBlock(parentRule);
1048
1053
  blocks.push(block);
1054
+ lastLayerParent?.childBlocks.push(block);
1049
1055
  sawLayers = true;
1050
1056
  lastLayers = layers;
1051
1057
  }
@@ -1055,12 +1061,12 @@ export class StylesSidebarPane extends Common.ObjectWrapper.eventMixin<EventType
1055
1061
  // We disable the layer widget initially. If we see a layer in
1056
1062
  // the matched styles we reenable the button.
1057
1063
  LayersWidget.ButtonProvider.instance().item().setVisible(false);
1058
-
1059
1064
  for (const style of matchedStyles.nodeStyles()) {
1060
1065
  const parentNode = matchedStyles.isInherited(style) ? matchedStyles.nodeForStyle(style) : null;
1061
1066
  if (parentNode && parentNode !== lastParentNode) {
1062
1067
  lastParentNode = parentNode;
1063
1068
  const block = await SectionBlock.createInheritedNodeBlock(lastParentNode);
1069
+ lastLayerParent = block;
1064
1070
  blocks.push(block);
1065
1071
  }
1066
1072
 
@@ -1078,6 +1084,7 @@ export class StylesSidebarPane extends Common.ObjectWrapper.eventMixin<EventType
1078
1084
  });
1079
1085
  }
1080
1086
  }
1087
+ lastLayerParent = undefined;
1081
1088
 
1082
1089
  const customHighlightPseudoRulesets: Array<{
1083
1090
  highlightName: string | null,
@@ -1130,9 +1137,11 @@ export class StylesSidebarPane extends Common.ObjectWrapper.eventMixin<EventType
1130
1137
  if (parentNode) {
1131
1138
  const block =
1132
1139
  await SectionBlock.createInheritedPseudoTypeBlock(pseudo.pseudoType, pseudo.highlightName, parentNode);
1140
+ lastLayerParent = block;
1133
1141
  blocks.push(block);
1134
1142
  } else {
1135
1143
  const block = SectionBlock.createPseudoTypeBlock(pseudo.pseudoType, pseudo.highlightName);
1144
+ lastLayerParent = block;
1136
1145
  blocks.push(block);
1137
1146
  }
1138
1147
  }
@@ -1503,6 +1512,7 @@ const MAX_LINK_LENGTH = 23;
1503
1512
  export class SectionBlock {
1504
1513
  readonly #titleElement: Element|null;
1505
1514
  sections: StylePropertiesSection[];
1515
+ childBlocks: SectionBlock[] = [];
1506
1516
  #expanded = false;
1507
1517
  #icon: Icon|undefined;
1508
1518
  constructor(titleElement: Element|null, expandable?: boolean, expandedByDefault?: boolean) {
@@ -1634,14 +1644,15 @@ export class SectionBlock {
1634
1644
  }
1635
1645
 
1636
1646
  updateFilter(): number {
1637
- let hasAnyVisibleSection = false;
1638
1647
  let numVisibleSections = 0;
1648
+ for (const childBlock of this.childBlocks) {
1649
+ numVisibleSections += childBlock.updateFilter();
1650
+ }
1639
1651
  for (const section of this.sections) {
1640
1652
  numVisibleSections += section.updateFilter() ? 1 : 0;
1641
- hasAnyVisibleSection = section.updateFilter() || hasAnyVisibleSection;
1642
1653
  }
1643
1654
  if (this.#titleElement) {
1644
- this.#titleElement.classList.toggle('hidden', !hasAnyVisibleSection);
1655
+ this.#titleElement.classList.toggle('hidden', numVisibleSections === 0);
1645
1656
  }
1646
1657
  return numVisibleSections;
1647
1658
  }
@@ -145,14 +145,15 @@ export class StatusDialog extends UI.Widget.VBox {
145
145
  }
146
146
 
147
147
  remove(): void {
148
- (this.element.parentNode as HTMLElement)?.classList.remove('tinted');
148
+ (this.element.parentNode as HTMLElement)?.classList.remove('opaque', 'tinted');
149
149
  this.stopTimer();
150
150
  this.element.remove();
151
151
  }
152
152
 
153
- showPane(parent: Element): void {
153
+ showPane(parent: Element, mode: 'tinted'|'opaque' = 'opaque'): void {
154
154
  this.show(parent);
155
- parent.classList.add('tinted');
155
+ parent.classList.toggle('tinted', mode === 'tinted');
156
+ parent.classList.toggle('opaque', mode === 'opaque');
156
157
  }
157
158
 
158
159
  enableAndFocusButton(): void {
@@ -423,28 +423,15 @@ export class TimelineFlameChartNetworkDataProvider implements PerfUI.FlameChart.
423
423
 
424
424
  /**
425
425
  * When users zoom in the flamechart, we only want to show them the network
426
- * requests between startTime and endTime. This function will call the
427
- * trackAppender to update the timeline data, and then force to create a new
428
- * PerfUI.FlameChart.FlameChartTimelineData instance to force the flamechart
429
- * to re-render.
426
+ * requests between startTime and endTime.
430
427
  */
431
428
  #updateTimelineData(startTime: Trace.Types.Timing.Milli, endTime: Trace.Types.Timing.Milli): void {
432
429
  if (!this.#networkTrackAppender || !this.#timelineData) {
433
430
  return;
434
431
  }
432
+ // This also has the side-effect of updating this.#timelineData with new
433
+ // information.
435
434
  this.#maxLevel = this.#networkTrackAppender.relayoutEntriesWithinBounds(this.#events, startTime, endTime);
436
-
437
- // TODO(crbug.com/1459225): Remove this recreating code.
438
- // Force to create a new PerfUI.FlameChart.FlameChartTimelineData instance
439
- // to force the flamechart to re-render. This also causes crbug.com/1459225.
440
- this.#timelineData = PerfUI.FlameChart.FlameChartTimelineData.create({
441
- entryLevels: this.#timelineData?.entryLevels,
442
- entryTotalTimes: this.#timelineData?.entryTotalTimes,
443
- entryStartTimes: this.#timelineData?.entryStartTimes,
444
- groups: this.#timelineData?.groups,
445
- initiatorsData: this.#timelineData.initiatorsData,
446
- entryDecorations: this.#timelineData.entryDecorations,
447
- });
448
435
  }
449
436
 
450
437
  /**
@@ -66,10 +66,12 @@ const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
66
66
  */
67
67
  export const SORT_ORDER_PAGE_LOAD_MARKERS: Readonly<Record<string, number>> = {
68
68
  [Trace.Types.Events.Name.NAVIGATION_START]: 0,
69
- [Trace.Types.Events.Name.MARK_LOAD]: 1,
70
- [Trace.Types.Events.Name.MARK_FCP]: 2,
71
- [Trace.Types.Events.Name.MARK_DOM_CONTENT]: 3,
72
- [Trace.Types.Events.Name.MARK_LCP_CANDIDATE]: 4,
69
+ [Trace.Types.Events.Name.SOFT_NAVIGATION_START]: 1,
70
+ [Trace.Types.Events.Name.MARK_LOAD]: 2,
71
+ [Trace.Types.Events.Name.MARK_FCP]: 3,
72
+ [Trace.Types.Events.Name.MARK_DOM_CONTENT]: 4,
73
+ [Trace.Types.Events.Name.MARK_LCP_CANDIDATE]: 5,
74
+ [Trace.Types.Events.Name.MARK_LCP_CANDIDATE_FOR_SOFT_NAVIGATION]: 6,
73
75
  };
74
76
 
75
77
  // Threshold to match up overlay markers that are off by a tiny amount so they aren't rendered
@@ -648,6 +650,7 @@ export class TimelineFlameChartView extends Common.ObjectWrapper.eventMixin<Even
648
650
  fieldMetricResult = fieldMetricResults.fcp;
649
651
  } else if (event.name === Trace.Types.Events.Name.MARK_LCP_CANDIDATE) {
650
652
  fieldMetricResult = fieldMetricResults.lcp;
653
+ // Ignoring soft-nav LCP on purpose.
651
654
  }
652
655
 
653
656
  if (!fieldMetricResult) {
@@ -669,7 +672,9 @@ export class TimelineFlameChartView extends Common.ObjectWrapper.eventMixin<Even
669
672
  // Set markers for Navigations, LCP, FCP, DCL, L.
670
673
  const markers = markerEvents.filter(
671
674
  event => event.name === Trace.Types.Events.Name.NAVIGATION_START ||
675
+ event.name === Trace.Types.Events.Name.SOFT_NAVIGATION_START ||
672
676
  event.name === Trace.Types.Events.Name.MARK_LCP_CANDIDATE ||
677
+ event.name === Trace.Types.Events.Name.MARK_LCP_CANDIDATE_FOR_SOFT_NAVIGATION ||
673
678
  event.name === Trace.Types.Events.Name.MARK_FCP ||
674
679
  event.name === Trace.Types.Events.Name.MARK_DOM_CONTENT ||
675
680
  event.name === Trace.Types.Events.Name.MARK_LOAD);
@@ -681,6 +686,7 @@ export class TimelineFlameChartView extends Common.ObjectWrapper.eventMixin<Even
681
686
  marker,
682
687
  parsedTrace.data.Meta.traceBounds,
683
688
  parsedTrace.data.Meta.navigationsByNavigationId,
689
+ parsedTrace.data.Meta.softNavigationsById,
684
690
  parsedTrace.data.Meta.navigationsByFrameId,
685
691
  );
686
692
  // If any of the markers overlap in timing, lets put them on the same marker.
@@ -732,24 +738,11 @@ export class TimelineFlameChartView extends Common.ObjectWrapper.eventMixin<Even
732
738
  entries.push(...Overlays.Overlays.entriesForOverlay(overlay));
733
739
  }
734
740
 
735
- // The insight's `relatedEvents` property likely already includes the events associated with
736
- // an overlay, but just in case not, include both arrays. Duplicates are fine.
737
- let relatedEventsList = this.#activeInsight?.model.relatedEvents;
738
- if (!relatedEventsList) {
739
- relatedEventsList = [];
740
- } else if (relatedEventsList instanceof Map) {
741
- relatedEventsList = Array.from(relatedEventsList.keys());
742
- }
743
- this.#dimInsightRelatedEvents([...entries, ...relatedEventsList]);
744
-
745
741
  if (options.updateTraceWindow) {
746
- // We should only expand the entry track when we are updating the trace window
747
- // (eg. when insight cards are initially opened).
742
+ // We should only expand the entry track when we are updating the trace window (eg. when insight cards are initially opened).
748
743
  // Otherwise the track will open when not intending to.
749
- for (const entry of entries) {
750
- // Ensure that the track for the entries are open.
751
- this.#expandEntryTrack(entry);
752
- }
744
+ this.#bulkExpandGroupsForEntries(entries);
745
+
753
746
  const overlaysBounds = Overlays.Overlays.traceWindowContainingOverlays(this.#currentInsightOverlays);
754
747
  if (overlaysBounds) {
755
748
  // Trace window covering all overlays expanded by 50% so that the overlays cover 2/3 (100/150) of the visible window. (Or use provided override)
@@ -767,11 +760,26 @@ export class TimelineFlameChartView extends Common.ObjectWrapper.eventMixin<Even
767
760
  }
768
761
  }
769
762
 
763
+ // The insight's `relatedEvents` property likely already includes the events associated with
764
+ // an overlay, but just in case not, include both arrays. Duplicates are fine.
765
+ let relatedEventsList = this.#activeInsight?.model.relatedEvents;
766
+ if (!relatedEventsList) {
767
+ relatedEventsList = [];
768
+ } else if (relatedEventsList instanceof Map) {
769
+ relatedEventsList = Array.from(relatedEventsList.keys());
770
+ }
771
+ this.#dimInsightRelatedEvents([...entries, ...relatedEventsList]);
772
+
770
773
  // Reveal entry if we have one.
774
+ // This is wrapped in a rAF to make sure the FlameChart draw from the
775
+ // expansion of any groups is complete - we need all the update() handlers
776
+ // to have run so the FlameChart has been drawn correctly at the right height.
771
777
  if (entries.length !== 0) {
772
778
  const earliestEntry =
773
779
  entries.reduce((earliest, current) => (earliest.ts < current.ts ? earliest : current), entries[0]);
774
- this.revealEventVertically(earliestEntry);
780
+ requestAnimationFrame(() => {
781
+ this.revealEventVertically(earliestEntry);
782
+ });
775
783
  }
776
784
  }
777
785
 
@@ -823,6 +831,41 @@ export class TimelineFlameChartView extends Common.ObjectWrapper.eventMixin<Even
823
831
  }
824
832
  }
825
833
 
834
+ /**
835
+ * Bulk expands the tracks (e.g. groups) that the given entries belong to.
836
+ * Will update them all at once and then do a redraw.
837
+ */
838
+ #bulkExpandGroupsForEntries(entries: Trace.Types.Events.Event[]): void {
839
+ const networkGroupIndexes = new Set<number>();
840
+ const mainGroupIndexes = new Set<number>();
841
+
842
+ for (const entry of entries) {
843
+ const chartName = Overlays.Overlays.chartForEntry(entry);
844
+ const provider = chartName === 'main' ? this.mainDataProvider : this.networkDataProvider;
845
+ const entryIndex = provider.indexForEvent?.(entry) ?? null;
846
+ if (entryIndex === null) {
847
+ continue;
848
+ }
849
+
850
+ const group = provider.groupForEvent?.(entryIndex) ?? null;
851
+ if (!group) {
852
+ continue;
853
+ }
854
+ if (group.expanded) {
855
+ continue;
856
+ }
857
+ const groupIndex = provider.timelineData().groups.indexOf(group);
858
+ if (chartName === 'main') {
859
+ mainGroupIndexes.add(groupIndex);
860
+ } else {
861
+ networkGroupIndexes.add(groupIndex);
862
+ }
863
+ }
864
+
865
+ this.mainFlameChart.bulkExpandGroups([...mainGroupIndexes]);
866
+ this.networkFlameChart.bulkExpandGroups([...networkGroupIndexes]);
867
+ }
868
+
826
869
  /**
827
870
  * Expands the track / group that the given entry is in.
828
871
  */