chrome-devtools-frontend 1.0.1700561 → 1.0.1701636

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 (53) hide show
  1. package/front_end/core/root/Runtime.ts +5 -0
  2. package/front_end/core/sdk/CSSPropertyParserMatchers.ts +16 -11
  3. package/front_end/core/sdk/NetworkManager.ts +7 -0
  4. package/front_end/core/sdk/SecurityOrigin.ts +1 -0
  5. package/front_end/core/sdk/SourceMap.ts +12 -1
  6. package/front_end/core/sdk/SourceMapCache.ts +12 -0
  7. package/front_end/core/sdk/SourceMapManager.ts +17 -8
  8. package/front_end/core/sdk/SourceMapScopeChainEntry.ts +2 -19
  9. package/front_end/core/sdk/SourceMapScopesInfo.ts +21 -0
  10. package/front_end/entrypoints/formatter_worker/ScopeParser.ts +7 -3
  11. package/front_end/entrypoints/formatter_worker/Substitute.ts +90 -26
  12. package/front_end/entrypoints/formatter_worker/formatter_worker-entrypoint.ts +1 -1
  13. package/front_end/entrypoints/main/ExecutionContextSelector.ts +17 -4
  14. package/front_end/generated/InspectorBackendCommands.ts +1 -3
  15. package/front_end/generated/protocol-mapping.d.ts +0 -8
  16. package/front_end/generated/protocol-proxy-api.d.ts +0 -6
  17. package/front_end/generated/protocol.ts +4 -22
  18. package/front_end/models/ai_assistance/debug.ts +10 -2
  19. package/front_end/models/comment_manager/CD4ABridge.ts +2 -2
  20. package/front_end/models/comment_manager/CommentManager.ts +3 -0
  21. package/front_end/models/formatter/FormatterWorkerPool.ts +2 -2
  22. package/front_end/models/javascript_metadata/NativeFunctions.ts +7 -2
  23. package/front_end/models/source_map_scopes/NamesResolver.ts +42 -39
  24. package/front_end/panels/accessibility/AccessibilityAnnouncementRecordingView.ts +70 -1
  25. package/front_end/panels/ai_assistance/components/ChatMessage.ts +1 -1
  26. package/front_end/panels/common/CommentsOverlayWidget.ts +29 -2
  27. package/front_end/panels/common/ExtensionServer.ts +18 -8
  28. package/front_end/panels/console/ConsolePrompt.ts +1 -1
  29. package/front_end/panels/console/ConsoleViewMessage.ts +1 -1
  30. package/front_end/panels/elements/ClassesPaneWidget.ts +2 -1
  31. package/front_end/panels/elements/DOMTreeContextMenu.ts +1 -1
  32. package/front_end/panels/elements/{ElementsTreeOutline.ts → DOMTreeWidget.ts} +34 -24
  33. package/front_end/panels/elements/ElementsPanel.ts +1 -1
  34. package/front_end/panels/elements/ElementsTreeElement.ts +1 -1
  35. package/front_end/panels/elements/ShortcutTreeElement.ts +1 -1
  36. package/front_end/panels/elements/StylePropertyTreeElement.ts +1 -1
  37. package/front_end/panels/elements/StylesSidebarPane.ts +4 -6
  38. package/front_end/panels/elements/TopLayerContainer.ts +1 -1
  39. package/front_end/panels/elements/elements.ts +4 -3
  40. package/front_end/panels/profiler/HeapDetachedElementsDataGrid.ts +1 -1
  41. package/front_end/panels/profiler/HeapProfileView.ts +8 -6
  42. package/front_end/panels/profiler/HeapSnapshotDataGrids.ts +8 -1
  43. package/front_end/panels/profiler/HeapSnapshotView.ts +6 -4
  44. package/front_end/panels/security/SecurityPanel.ts +87 -99
  45. package/front_end/panels/sensors/SensorsView.ts +87 -0
  46. package/front_end/panels/sensors/sensors-meta.ts +69 -1
  47. package/front_end/panels/settings/keybindsSettingsTab.css +1 -1
  48. package/front_end/third_party/chromium/README.chromium +1 -1
  49. package/front_end/ui/legacy/Treeoutline.ts +2 -1
  50. package/front_end/ui/legacy/components/data_grid/DataGridElement.ts +1 -1
  51. package/front_end/ui/legacy/treeoutline.css +2 -2
  52. package/front_end/ui/visual_logging/KnownContextValues.ts +7 -0
  53. package/package.json +1 -1
@@ -75,7 +75,7 @@ import {ImagePreviewPopover} from './ImagePreviewPopover.js';
75
75
  import {ShortcutTreeElement} from './ShortcutTreeElement.js';
76
76
  import {TopLayerContainer} from './TopLayerContainer.js';
77
77
 
78
- const {html, nothing, render, Directives: {classMap, repeat, styleMap}} = Lit;
78
+ const {html, nothing, render, Directives: {classMap, ifDefined, repeat, styleMap}} = Lit;
79
79
 
80
80
  const UIStrings = {
81
81
  /**
@@ -101,7 +101,7 @@ const UIStrings = {
101
101
  */
102
102
  reveal: 'reveal',
103
103
  } as const;
104
- const str_ = i18n.i18n.registerUIStrings('panels/elements/ElementsTreeOutline.ts', UIStrings);
104
+ const str_ = i18n.i18n.registerUIStrings('panels/elements/DOMTreeWidget.ts', UIStrings);
105
105
  const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
106
106
  const elementsTreeOutlineByDOMModel = new WeakMap<SDK.DOMModel.DOMModel, ElementsTreeOutline>();
107
107
 
@@ -919,6 +919,8 @@ export const DECLARATIVE_VIEW: View = (input: ViewInput, _output: ViewOutput, ta
919
919
  // clang-format off
920
920
  return html`
921
921
  <li role="treeitem"
922
+ data-backend-node-id=${ifDefined(node.backendNodeId())}
923
+ data-target-id=${ifDefined(node.domModel().target().id())}
922
924
  selectable=${input.selectEnabled ? 'true' : 'false'}
923
925
  ?selected=${isSelected && !input.selectedClosingTag}
924
926
  class=${classes}
@@ -997,6 +999,8 @@ export const DECLARATIVE_VIEW: View = (input: ViewInput, _output: ViewOutput, ta
997
999
  ${node instanceof SDK.DOMModel.DOMDocument ? renderTopLayerContainer(node, depth + 1) : nothing}
998
1000
  ${needsClosingTag ? html`
999
1001
  <li role="treeitem"
1002
+ data-backend-node-id=${ifDefined(node.backendNodeId())}
1003
+ data-target-id=${ifDefined(node.domModel().target().id())}
1000
1004
  selectable=${input.selectEnabled ? 'true' : 'false'}
1001
1005
  ?selected=${isSelected && Boolean(input.selectedClosingTag)}
1002
1006
  class=${classMap({
@@ -1348,7 +1352,7 @@ export class DOMTreeWidget extends UI.Widget.Widget {
1348
1352
  element?: HTMLElement,
1349
1353
  [changeTracker]:
1350
1354
  UI.Widget.WidgetDependencies<typeof DOMTreeWidget>|[ChangeTracker.ChangeTracker.ChangeTracker?] = [],
1351
- view: View = DEFAULT_VIEW,
1355
+ view: View = DECLARATIVE_VIEW,
1352
1356
  ) {
1353
1357
  super(element, {
1354
1358
  useShadowDom: false,
@@ -1358,12 +1362,12 @@ export class DOMTreeWidget extends UI.Widget.Widget {
1358
1362
  this.#changeTracker = changeTracker;
1359
1363
  this.#showHTMLCommentsSetting.addChangeListener(this.#onShowHTMLCommentsChange, this);
1360
1364
  if (Common.Settings.Settings.instance().moduleSetting('highlight-node-on-hover-in-overlay').get()) {
1361
- SDK.TargetManager.TargetManager.instance().addModelListener(
1362
- SDK.OverlayModel.OverlayModel, SDK.OverlayModel.Events.HIGHLIGHT_NODE_REQUESTED, this.#highlightNode, this,
1363
- {scoped: true});
1364
- SDK.TargetManager.TargetManager.instance().addModelListener(
1365
- SDK.OverlayModel.OverlayModel, SDK.OverlayModel.Events.INSPECT_MODE_WILL_BE_TOGGLED,
1366
- this.#clearHighlightedNode, this, {scoped: true});
1365
+ SDK.TargetManager.TargetManager.instance().addModelListener(SDK.OverlayModel.OverlayModel,
1366
+ SDK.OverlayModel.Events.HIGHLIGHT_NODE_REQUESTED,
1367
+ this.#highlightNode, this, {scoped: true});
1368
+ SDK.TargetManager.TargetManager.instance().addModelListener(SDK.OverlayModel.OverlayModel,
1369
+ SDK.OverlayModel.Events.INSPECT_MODE_WILL_BE_TOGGLED,
1370
+ this.#clearHighlightedNode, this, {scoped: true});
1367
1371
  }
1368
1372
  this.#setupPopovers();
1369
1373
  }
@@ -2245,7 +2249,7 @@ export class DOMTreeWidget extends UI.Widget.Widget {
2245
2249
  this.onDocumentUpdated(domModel);
2246
2250
  } else {
2247
2251
  void domModel.requestDocument().then(document => {
2248
- if (document && this.isShowing()) {
2252
+ if (document && this.isShowing() && this.#wiredDOMModels.has(domModel)) {
2249
2253
  this.rootDOMNode = document;
2250
2254
  this.onDocumentUpdated(domModel);
2251
2255
  }
@@ -2269,6 +2273,12 @@ export class DOMTreeWidget extends UI.Widget.Widget {
2269
2273
  this.#wiredDOMModels.delete(domModel);
2270
2274
  this.#unwireDOMModel(domModel);
2271
2275
  }
2276
+ if (this.#rootDOMNode?.domModel() === domModel) {
2277
+ this.#rootDOMNode = null;
2278
+ this.#selectedDOMNode = null;
2279
+ this.#expandedNodes.clear();
2280
+ this.#updateRecords.clear();
2281
+ }
2272
2282
  this.performUpdate();
2273
2283
  return;
2274
2284
  }
@@ -3141,7 +3151,7 @@ export class DOMTreeWidget extends UI.Widget.Widget {
3141
3151
  this.onDocumentUpdated(domModel);
3142
3152
  } else if (this.#view === DECLARATIVE_VIEW) {
3143
3153
  void domModel.requestDocument().then(document => {
3144
- if (document && this.isShowing()) {
3154
+ if (document && this.isShowing() && this.#wiredDOMModels.has(domModel)) {
3145
3155
  this.rootDOMNode = document;
3146
3156
  this.onDocumentUpdated(domModel);
3147
3157
  }
@@ -3472,8 +3482,8 @@ export class ElementsTreeOutline extends ElementsTreeOutlineBase {
3472
3482
  }
3473
3483
 
3474
3484
  selectedNodeChanged(focus: boolean): void {
3475
- this.dispatchEventToListeners(
3476
- ElementsTreeOutline.Events.SelectedNodeChanged, {node: this.selectedDOMNodeInternal, focus});
3485
+ this.dispatchEventToListeners(ElementsTreeOutline.Events.SelectedNodeChanged,
3486
+ {node: this.selectedDOMNodeInternal, focus});
3477
3487
  }
3478
3488
 
3479
3489
  private fireElementsTreeUpdated(nodes: SDK.DOMModel.DOMNode[]): void {
@@ -3787,8 +3797,8 @@ export class ElementsTreeOutline extends ElementsTreeOutlineBase {
3787
3797
  domModel.addEventListener(SDK.DOMModel.Events.DocumentURLChanged, this.documentURLChanged, this);
3788
3798
  domModel.addEventListener(SDK.DOMModel.Events.ChildNodeCountUpdated, this.childNodeCountUpdated, this);
3789
3799
  domModel.addEventListener(SDK.DOMModel.Events.DistributedNodesChanged, this.distributedNodesChanged, this);
3790
- domModel.addEventListener(
3791
- SDK.DOMModel.Events.AffectedByStartingStylesFlagUpdated, this.affectedByStartingStylesFlagUpdated, this);
3800
+ domModel.addEventListener(SDK.DOMModel.Events.AffectedByStartingStylesFlagUpdated,
3801
+ this.affectedByStartingStylesFlagUpdated, this);
3792
3802
  domModel.addEventListener(SDK.DOMModel.Events.AdoptedStyleSheetsModified, this.adoptedStyleSheetsModified, this);
3793
3803
  }
3794
3804
 
@@ -3803,8 +3813,8 @@ export class ElementsTreeOutline extends ElementsTreeOutlineBase {
3803
3813
  domModel.removeEventListener(SDK.DOMModel.Events.DocumentURLChanged, this.documentURLChanged, this);
3804
3814
  domModel.removeEventListener(SDK.DOMModel.Events.ChildNodeCountUpdated, this.childNodeCountUpdated, this);
3805
3815
  domModel.removeEventListener(SDK.DOMModel.Events.DistributedNodesChanged, this.distributedNodesChanged, this);
3806
- domModel.removeEventListener(
3807
- SDK.DOMModel.Events.AffectedByStartingStylesFlagUpdated, this.affectedByStartingStylesFlagUpdated, this);
3816
+ domModel.removeEventListener(SDK.DOMModel.Events.AffectedByStartingStylesFlagUpdated,
3817
+ this.affectedByStartingStylesFlagUpdated, this);
3808
3818
  domModel.removeEventListener(SDK.DOMModel.Events.AdoptedStyleSheetsModified, this.adoptedStyleSheetsModified, this);
3809
3819
  elementsTreeOutlineByDOMModel.delete(domModel);
3810
3820
  }
@@ -4026,8 +4036,8 @@ export class ElementsTreeOutline extends ElementsTreeOutlineBase {
4026
4036
  return false;
4027
4037
  }
4028
4038
 
4029
- private createElementTreeElement(node: SDK.DOMModel.DOMNode|SDK.DOMModel.AdoptedStyleSheet[], isClosingTag?: boolean):
4030
- UI.TreeOutline.TreeElement {
4039
+ private createElementTreeElement(node: SDK.DOMModel.DOMNode|SDK.DOMModel.AdoptedStyleSheet[],
4040
+ isClosingTag?: boolean): UI.TreeOutline.TreeElement {
4031
4041
  if (node instanceof Array) {
4032
4042
  return new AdoptedStyleSheetSetTreeElement(node);
4033
4043
  }
@@ -4202,9 +4212,9 @@ export class ElementsTreeOutline extends ElementsTreeOutlineBase {
4202
4212
  this.#updateChildren(treeElement);
4203
4213
  }
4204
4214
 
4205
- insertChildElement(
4206
- treeElement: ElementsTreeElement|TopLayerContainer, child: SDK.DOMModel.DOMNode|SDK.DOMModel.AdoptedStyleSheet[],
4207
- index: number, isClosingTag?: boolean): UI.TreeOutline.TreeElement {
4215
+ insertChildElement(treeElement: ElementsTreeElement|TopLayerContainer,
4216
+ child: SDK.DOMModel.DOMNode|SDK.DOMModel.AdoptedStyleSheet[], index: number,
4217
+ isClosingTag?: boolean): UI.TreeOutline.TreeElement {
4208
4218
  const newElement = this.createElementTreeElement(child, isClosingTag);
4209
4219
  treeElement.insertChild(newElement, index);
4210
4220
  return newElement;
@@ -4237,8 +4247,8 @@ export class ElementsTreeOutline extends ElementsTreeOutlineBase {
4237
4247
 
4238
4248
  // Remove any tree elements that no longer have this node as their parent and save
4239
4249
  // all existing elements that could be reused. This also removes closing tag element.
4240
- const existingTreeElements = new Map<
4241
- SDK.DOMModel.DOMNode|SDK.DOMModel.AdoptedStyleSheet[], UI.TreeOutline.TreeElement&ElementsTreeElement>();
4250
+ const existingTreeElements = new Map<SDK.DOMModel.DOMNode|SDK.DOMModel.AdoptedStyleSheet[],
4251
+ UI.TreeOutline.TreeElement&ElementsTreeElement>();
4242
4252
  for (let i = treeElement.childCount() - 1; i >= 0; --i) {
4243
4253
  const existingTreeElement = treeElement.childAt(i);
4244
4254
  if (!(existingTreeElement instanceof ElementsTreeElement)) {
@@ -51,8 +51,8 @@ import {AccessibilityTreeView} from './AccessibilityTreeView.js';
51
51
  import {ColorSwatchPopoverIcon} from './ColorSwatchPopoverIcon.js';
52
52
  import * as ElementsComponents from './components/components.js';
53
53
  import {ComputedStyleWidget} from './ComputedStyleWidget.js';
54
+ import {DOMTreeWidget, type ElementsTreeOutline} from './DOMTreeWidget.js';
54
55
  import elementsPanelStyles from './elementsPanel.css.js';
55
- import {DOMTreeWidget, type ElementsTreeOutline} from './ElementsTreeOutline.js';
56
56
  import {LayoutPane} from './LayoutPane.js';
57
57
  import type {MarkerDecorator} from './MarkerDecorator.js';
58
58
  import {MetricsSidebarPane} from './MetricsSidebarPane.js';
@@ -67,10 +67,10 @@ import * as Media from '../media/media.js';
67
67
 
68
68
  import * as ElementsComponents from './components/components.js';
69
69
  import {cssPath} from './DOMPath.js';
70
+ import {type ElementsTreeOutline, MappedCharToEntity} from './DOMTreeWidget.js';
70
71
  import {getElementIssueDetails} from './ElementIssueUtils.js';
71
72
  import {ElementsPanel} from './ElementsPanel.js';
72
73
  import * as ElementStatePaneWidget from './ElementStatePaneWidget.js';
73
- import {type ElementsTreeOutline, MappedCharToEntity} from './ElementsTreeOutline.js';
74
74
  import {ImagePreviewPopover} from './ImagePreviewPopover.js';
75
75
  import {getRegisteredDecorators, type MarkerDecorator, type MarkerDecoratorRegistration} from './MarkerDecorator.js';
76
76
 
@@ -40,8 +40,8 @@ import * as Lit from '../../ui/lit/lit.js';
40
40
  import * as VisualElements from '../../ui/visual_logging/visual_logging.js';
41
41
 
42
42
  import * as ElementsComponents from './components/components.js';
43
+ import {ElementsTreeOutline} from './DOMTreeWidget.js';
43
44
  import {adornerRef, ElementsTreeElement, handleAdornerKeydown} from './ElementsTreeElement.js';
44
- import {ElementsTreeOutline} from './ElementsTreeOutline.js';
45
45
 
46
46
  const {html, render} = Lit;
47
47
 
@@ -2044,7 +2044,7 @@ export class AnchorFunctionRenderer extends AnchorFunctionRendererBase {
2044
2044
 
2045
2045
  override render(match: SDK.CSSPropertyParserMatchers.AnchorFunctionMatch, context: RenderingContext): Node[] {
2046
2046
  const content = document.createElement('span');
2047
- if (match.node.name === 'VariableName') {
2047
+ if (match.node.name !== 'CallExpression') {
2048
2048
  // Link an anchor double-dashed ident to its matching anchor element.
2049
2049
  void AnchorFunctionRenderer.decorateAnchorForAnchorLink(this.#stylesContainer, content, {identifier: match.text});
2050
2050
  } else {
@@ -729,9 +729,6 @@ export class StylesSidebarPane extends StylesSidebarPaneBase implements StylesCo
729
729
  }
730
730
 
731
731
  setEditingStyle(editing: boolean): void {
732
- if (editing) {
733
- this.isSuppressingResets = false;
734
- }
735
732
  if (this.isEditingStyle === editing) {
736
733
  return;
737
734
  }
@@ -816,12 +813,13 @@ export class StylesSidebarPane extends StylesSidebarPaneBase implements StylesCo
816
813
  #scheduleResetUpdateIfNotEditing(): void {
817
814
  this.scheduleResetUpdateIfNotEditingCalledForTest();
818
815
 
819
- if (this.isSuppressingResets) {
816
+ // Don't schedule if editing; the edit completion will handle the update.
817
+ if (this.userOperation || this.isEditingStyle) {
820
818
  return;
821
819
  }
822
820
 
823
- // Don't schedule if editing; the edit completion will handle the update.
824
- if (this.userOperation || this.isEditingStyle) {
821
+ if (this.isSuppressingResets) {
822
+ this.isSuppressingResets = false;
825
823
  return;
826
824
  }
827
825
 
@@ -6,7 +6,7 @@ import type * as Common from '../../core/common/common.js';
6
6
  import * as SDK from '../../core/sdk/sdk.js';
7
7
  import * as UI from '../../ui/legacy/legacy.js';
8
8
 
9
- import type {ElementsTreeOutline} from './ElementsTreeOutline.js';
9
+ import type {ElementsTreeOutline} from './DOMTreeWidget.js';
10
10
  import {ShortcutTreeElement} from './ShortcutTreeElement.js';
11
11
 
12
12
  export class TopLayerContainer extends UI.TreeOutline.TreeElement {
@@ -10,7 +10,7 @@ import './ElementsSidebarPane.js';
10
10
  import './ElementsTreeElement.js';
11
11
  import './AdoptedStyleSheetTreeElement.js';
12
12
  import './TopLayerContainer.js';
13
- import './ElementsTreeOutline.js';
13
+ import './DOMTreeWidget.js';
14
14
  import './ImagePreviewPopover.js';
15
15
  import './EventListenersWidget.js';
16
16
  import './MarkerDecorator.js';
@@ -42,11 +42,11 @@ import * as CSSSpecificityBreakdown from './CSSSpecificityBreakdown.js';
42
42
  import * as CSSValueTraceView from './CSSValueTraceView.js';
43
43
  import * as DOMPath from './DOMPath.js';
44
44
  import * as DOMTreeContextMenu from './DOMTreeContextMenu.js';
45
+ import * as DOMTreeWidget from './DOMTreeWidget.js';
45
46
  import * as ElementsPanel from './ElementsPanel.js';
46
47
  import * as ElementsSidebarPane from './ElementsSidebarPane.js';
47
48
  import * as ElementStatePaneWidget from './ElementStatePaneWidget.js';
48
49
  import * as ElementsTreeElement from './ElementsTreeElement.js';
49
- import * as ElementsTreeOutline from './ElementsTreeOutline.js';
50
50
  import * as EventListenersWidget from './EventListenersWidget.js';
51
51
  import * as ImagePreviewPopover from './ImagePreviewPopover.js';
52
52
  import * as InspectElementModeController from './InspectElementModeController.js';
@@ -80,11 +80,12 @@ export {
80
80
  CSSValueTraceView,
81
81
  DOMPath,
82
82
  DOMTreeContextMenu,
83
+ DOMTreeWidget,
84
+ DOMTreeWidget as ElementsTreeOutline,
83
85
  ElementsPanel,
84
86
  ElementsSidebarPane,
85
87
  ElementStatePaneWidget,
86
88
  ElementsTreeElement,
87
- ElementsTreeOutline,
88
89
  EventListenersWidget,
89
90
  ImagePreviewPopover,
90
91
  InspectElementModeController,
@@ -64,7 +64,7 @@ const DEFAULT_VIEW = (input: ViewInput, output: ViewOutput, target: HTMLElement|
64
64
  <tr jslog=${VisualLogging.tableRow('detached-element')}>
65
65
  <td>
66
66
  <devtools-widget
67
- ${widget(Elements.ElementsTreeOutline.DOMTreeWidget, {
67
+ ${widget(Elements.DOMTreeWidget.DOMTreeWidget, {
68
68
  omitRootDOMNode: false,
69
69
  selectEnabled: true,
70
70
  hideGutter: true,
@@ -1067,12 +1067,14 @@ export class SamplingHeapProfileType extends SamplingHeapProfileTypeBase {
1067
1067
  }
1068
1068
 
1069
1069
  override customContent(): Element|null {
1070
- const checkboxSetting = SettingsUI.SettingsUI.createSettingCheckbox(
1071
- i18nString(UIStrings.samplingHeapProfilerTimeline), this.#recordTimelineSetting);
1072
- this.customContentInternal = checkboxSetting;
1073
- checkboxSetting.setAttribute(
1074
- 'jslog', `${VisualLogging.toggle('record-sampling-heap-profiler-timeline').track({click: true})}`);
1075
- return checkboxSetting;
1070
+ if (!this.customContentInternal) {
1071
+ const checkboxSetting = SettingsUI.SettingsUI.createSettingCheckbox(
1072
+ i18nString(UIStrings.samplingHeapProfilerTimeline), this.#recordTimelineSetting);
1073
+ this.customContentInternal = checkboxSetting;
1074
+ checkboxSetting.setAttribute(
1075
+ 'jslog', `${VisualLogging.toggle('record-sampling-heap-profiler-timeline').track({click: true})}`);
1076
+ }
1077
+ return this.customContentInternal;
1076
1078
  }
1077
1079
 
1078
1080
  override setCustomContentEnabled(enable: boolean): void {
@@ -532,19 +532,26 @@ export class HeapSnapshotViewportDataGrid extends HeapSnapshotSortableDataGrid {
532
532
  const scrollTop = this.scrollContainer.scrollTop;
533
533
  const scrollBottom = scrollTop + this.scrollContainer.offsetHeight;
534
534
  if (height >= scrollTop && height < scrollBottom) {
535
+ if (!node.dataGrid) {
536
+ this.updateVisibleNodes(false);
537
+ }
535
538
  return Promise.resolve(node);
536
539
  }
537
540
 
538
541
  const scrollGap = 40;
539
542
  this.scrollContainer.scrollTop = Math.max(0, height - scrollGap);
543
+ this.updateVisibleNodes(false);
540
544
  return new Promise(resolve => {
541
- console.assert(!this.scrollToResolveCallback);
545
+ if (this.scrollToResolveCallback) {
546
+ this.scrollToResolveCallback();
547
+ }
542
548
  this.scrollToResolveCallback = resolve.bind(null, node);
543
549
  // Still resolve the promise if it does not scroll for some reason.
544
550
  this.scrollContainer.window().requestAnimationFrame(() => {
545
551
  if (!this.scrollToResolveCallback) {
546
552
  return;
547
553
  }
554
+ this.updateVisibleNodes(false);
548
555
  this.scrollToResolveCallback();
549
556
  this.scrollToResolveCallback = null;
550
557
  });
@@ -1640,10 +1640,12 @@ export class TrackingHeapSnapshotProfileType extends TrackingHeapSnapshotProfile
1640
1640
  }
1641
1641
 
1642
1642
  override customContent(): Element|null {
1643
- const checkboxSetting = SettingsUI.SettingsUI.createSettingCheckbox(
1644
- i18nString(UIStrings.recordAllocationStacksExtra), this.recordAllocationStacksSettingInternal);
1645
- this.customContentInternal = (checkboxSetting);
1646
- return checkboxSetting;
1643
+ if (!this.customContentInternal) {
1644
+ const checkboxSetting = SettingsUI.SettingsUI.createSettingCheckbox(
1645
+ i18nString(UIStrings.recordAllocationStacksExtra), this.recordAllocationStacksSettingInternal);
1646
+ this.customContentInternal = checkboxSetting;
1647
+ }
1648
+ return this.customContentInternal;
1647
1649
  }
1648
1650
 
1649
1651
  override setCustomContentEnabled(enable: boolean): void {
@@ -13,7 +13,7 @@ import * as NetworkForward from '../../panels/network/forward/forward.js';
13
13
  import * as Buttons from '../../ui/components/buttons/buttons.js';
14
14
  import {createIcon, type Icon} from '../../ui/kit/kit.js';
15
15
  import * as UI from '../../ui/legacy/legacy.js';
16
- import {Directives, html, nothing, render, type TemplateResult} from '../../ui/lit/lit.js';
16
+ import {Directives, html, type LitTemplate, nothing, render, type TemplateResult} from '../../ui/lit/lit.js';
17
17
  import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';
18
18
 
19
19
  import lockIconStyles from './lockIcon.css.js';
@@ -1347,10 +1347,10 @@ interface DetailsTableRow {
1347
1347
  value: string|TemplateResult;
1348
1348
  }
1349
1349
 
1350
- function renderDetailsTable(rows: DetailsTableRow[]): TemplateResult {
1350
+ function renderDetailsTable(rows: DetailsTableRow[], additionalClasses: Directives.ClassInfo = {}): TemplateResult {
1351
1351
  // clang-format off
1352
1352
  return html`
1353
- <table class="details-table">
1353
+ <table class=${Directives.classMap({'details-table': true, ...additionalClasses})}>
1354
1354
  ${rows.map(row => html`
1355
1355
  <tr class="details-table-row">
1356
1356
  <td>${row.key ?? nothing}</td>
@@ -1492,6 +1492,74 @@ function renderCertificateSection(input: CertificateSectionInput): TemplateResul
1492
1492
  // clang-format on
1493
1493
  }
1494
1494
 
1495
+ interface CertificateTransparencySectionInput {
1496
+ securityDetails: Protocol.Network.SecurityDetails;
1497
+ isDetailsShown: boolean;
1498
+ onToggleDetails: () => void;
1499
+ }
1500
+
1501
+ function renderSctSummary(scts: Protocol.Network.SignedCertificateTimestamp[], isShown: boolean): TemplateResult {
1502
+ const rows = scts.map(sct => ({
1503
+ key: i18nString(UIStrings.sct),
1504
+ value: `${sct.logDescription} (${sct.origin}, ${sct.status})`,
1505
+ }));
1506
+ return renderDetailsTable(rows, {'sct-summary': true, hidden: !isShown});
1507
+ }
1508
+
1509
+ function renderSctDetails(scts: Protocol.Network.SignedCertificateTimestamp[], isShown: boolean): TemplateResult {
1510
+ // clang-format off
1511
+ return html`
1512
+ <div class=${Directives.classMap({'sct-details': true, hidden: !isShown})}>
1513
+ ${scts.map(sct => renderDetailsTable([
1514
+ {key: i18nString(UIStrings.logName), value: sct.logDescription},
1515
+ {key: i18nString(UIStrings.logId), value: sct.logId.replace(/(.{2})/g, '$1 ')},
1516
+ {key: i18nString(UIStrings.validationStatus), value: sct.status},
1517
+ {key: i18nString(UIStrings.source), value: sct.origin},
1518
+ {key: i18nString(UIStrings.issuedAt), value: new Date(sct.timestamp).toUTCString()},
1519
+ {key: i18nString(UIStrings.hashAlgorithm), value: sct.hashAlgorithm},
1520
+ {key: i18nString(UIStrings.signatureAlgorithm), value: sct.signatureAlgorithm},
1521
+ {key: i18nString(UIStrings.signatureData), value: sct.signatureData.replace(/(.{2})/g, '$1 ')},
1522
+ ]))}
1523
+ </div>`;
1524
+ // clang-format on
1525
+ }
1526
+
1527
+ function renderCertificateTransparencyNote(compliance: Protocol.Network.CertificateTransparencyCompliance):
1528
+ LitTemplate {
1529
+ if (compliance === Protocol.Network.CertificateTransparencyCompliance.Unknown) {
1530
+ return nothing;
1531
+ }
1532
+
1533
+ const note = compliance === Protocol.Network.CertificateTransparencyCompliance.Compliant ?
1534
+ i18nString(UIStrings.thisRequestCompliesWithChromes) :
1535
+ i18nString(UIStrings.thisRequestDoesNotComplyWith);
1536
+
1537
+ return html`<div class="origin-view-section-notes">${note}</div>`;
1538
+ }
1539
+
1540
+ function renderCertificateTransparencySection(input: CertificateTransparencySectionInput): TemplateResult {
1541
+ const {securityDetails, isDetailsShown, onToggleDetails} = input;
1542
+ const scts = securityDetails.signedCertificateTimestampList;
1543
+ const toggleButtonText =
1544
+ isDetailsShown ? i18nString(UIStrings.hideFullDetails) : i18nString(UIStrings.showFullDetails);
1545
+
1546
+ // clang-format off
1547
+ return html`
1548
+ <div class="origin-view-section-title" role="heading" aria-level="2">${i18nString(UIStrings.certificateTransparency)}</div>
1549
+ ${renderSctSummary(scts, !isDetailsShown)}
1550
+ ${renderSctDetails(scts, isDetailsShown)}
1551
+ ${scts.length ? html`
1552
+ <devtools-button
1553
+ class="details-toggle"
1554
+ .variant=${Buttons.Button.Variant.OUTLINED}
1555
+ .accessibleLabel=${toggleButtonText}
1556
+ .accessibleExpanded=${isDetailsShown}
1557
+ .jslogContext=${'security.toggle-scts-details'}
1558
+ @click=${onToggleDetails}>${toggleButtonText}</devtools-button>` : nothing}
1559
+ ${renderCertificateTransparencyNote(securityDetails.certificateTransparencyCompliance)}`;
1560
+ // clang-format on
1561
+ }
1562
+
1495
1563
  export class SecurityOriginView extends UI.Widget.VBox {
1496
1564
  readonly #origin: Platform.DevToolsPath.UrlString;
1497
1565
  readonly #titleSection: HTMLElement;
@@ -1516,80 +1584,10 @@ export class SecurityOriginView extends UI.Widget.VBox {
1516
1584
 
1517
1585
  const sctListLength = originState.securityDetails.signedCertificateTimestampList.length;
1518
1586
  const ctCompliance = originState.securityDetails.certificateTransparencyCompliance;
1519
- let sctSection;
1520
- if (sctListLength || ctCompliance !== Protocol.Network.CertificateTransparencyCompliance.Unknown) {
1521
- // Create the Certificate Transparency section outside the callback, so that it appears in the right place.
1522
- sctSection = this.element.createChild('div', 'origin-view-section');
1523
- const sctDiv = sctSection.createChild('div', 'origin-view-section-title');
1524
- sctDiv.textContent = i18nString(UIStrings.certificateTransparency);
1525
- UI.ARIAUtils.markAsHeading(sctDiv, 2);
1526
- }
1527
-
1528
- if (!sctSection) {
1587
+ if (!sctListLength && ctCompliance === Protocol.Network.CertificateTransparencyCompliance.Unknown) {
1529
1588
  return;
1530
1589
  }
1531
-
1532
- // Show summary of SCT(s) of Certificate Transparency.
1533
- const sctSummaryTable = new SecurityDetailsTable();
1534
- sctSummaryTable.element().classList.add('sct-summary');
1535
- sctSection.appendChild(sctSummaryTable.element());
1536
- for (let i = 0; i < sctListLength; i++) {
1537
- const sct = originState.securityDetails.signedCertificateTimestampList[i];
1538
- sctSummaryTable.addRow(
1539
- i18nString(UIStrings.sct), sct.logDescription + ' (' + sct.origin + ', ' + sct.status + ')');
1540
- }
1541
-
1542
- // Show detailed SCT(s) of Certificate Transparency.
1543
- const sctTableWrapper = sctSection.createChild('div', 'sct-details');
1544
- sctTableWrapper.classList.add('hidden');
1545
- for (let i = 0; i < sctListLength; i++) {
1546
- const sctTable = new SecurityDetailsTable();
1547
- sctTableWrapper.appendChild(sctTable.element());
1548
- const sct = originState.securityDetails.signedCertificateTimestampList[i];
1549
- sctTable.addRow(i18nString(UIStrings.logName), sct.logDescription);
1550
- sctTable.addRow(i18nString(UIStrings.logId), sct.logId.replace(/(.{2})/g, '$1 '));
1551
- sctTable.addRow(i18nString(UIStrings.validationStatus), sct.status);
1552
- sctTable.addRow(i18nString(UIStrings.source), sct.origin);
1553
- sctTable.addRow(i18nString(UIStrings.issuedAt), new Date(sct.timestamp).toUTCString());
1554
- sctTable.addRow(i18nString(UIStrings.hashAlgorithm), sct.hashAlgorithm);
1555
- sctTable.addRow(i18nString(UIStrings.signatureAlgorithm), sct.signatureAlgorithm);
1556
- sctTable.addRow(i18nString(UIStrings.signatureData), sct.signatureData.replace(/(.{2})/g, '$1 '));
1557
- }
1558
-
1559
- // Add link to toggle between displaying of the summary of the SCT(s) and the detailed SCT(s).
1560
- if (sctListLength) {
1561
- function toggleSctDetailsDisplay(): void {
1562
- let buttonText;
1563
- const isDetailsShown = !sctTableWrapper.classList.contains('hidden');
1564
- if (isDetailsShown) {
1565
- buttonText = i18nString(UIStrings.showFullDetails);
1566
- } else {
1567
- buttonText = i18nString(UIStrings.hideFullDetails);
1568
- }
1569
- toggleSctsDetailsLink.textContent = buttonText;
1570
- UI.ARIAUtils.setLabel(toggleSctsDetailsLink, buttonText);
1571
- UI.ARIAUtils.setExpanded(toggleSctsDetailsLink, !isDetailsShown);
1572
- sctSummaryTable.element().classList.toggle('hidden');
1573
- sctTableWrapper.classList.toggle('hidden');
1574
- }
1575
- const toggleSctsDetailsLink = UI.UIUtils.createTextButton(
1576
- i18nString(UIStrings.showFullDetails), toggleSctDetailsDisplay,
1577
- {className: 'details-toggle', jslogContext: 'security.toggle-scts-details'});
1578
- sctSection.appendChild(toggleSctsDetailsLink);
1579
- }
1580
-
1581
- switch (ctCompliance) {
1582
- case Protocol.Network.CertificateTransparencyCompliance.Compliant:
1583
- sctSection.createChild('div', 'origin-view-section-notes').textContent =
1584
- i18nString(UIStrings.thisRequestCompliesWithChromes);
1585
- break;
1586
- case Protocol.Network.CertificateTransparencyCompliance.NotCompliant:
1587
- sctSection.createChild('div', 'origin-view-section-notes').textContent =
1588
- i18nString(UIStrings.thisRequestDoesNotComplyWith);
1589
- break;
1590
- case Protocol.Network.CertificateTransparencyCompliance.Unknown:
1591
- break;
1592
- }
1590
+ this.#createCertificateTransparencySection(originState.securityDetails);
1593
1591
 
1594
1592
  const noteSection = this.element.createChild('div', 'origin-view-section origin-view-notes');
1595
1593
  if (originState.loadedFromCache) {
@@ -1640,6 +1638,20 @@ export class SecurityOriginView extends UI.Widget.VBox {
1640
1638
  updateCertificateSection();
1641
1639
  }
1642
1640
 
1641
+ #createCertificateTransparencySection(securityDetails: Protocol.Network.SecurityDetails): void {
1642
+ const section = this.element.createChild('div', 'origin-view-section certificate-transparency-section');
1643
+ let isDetailsShown = false;
1644
+ const onToggleDetails = (): void => {
1645
+ isDetailsShown = !isDetailsShown;
1646
+ updateSection();
1647
+ };
1648
+ const updateSection = (): void => {
1649
+ // eslint-disable-next-line @devtools/no-lit-render-outside-of-view
1650
+ render(renderCertificateTransparencySection({securityDetails, isDetailsShown, onToggleDetails}), section);
1651
+ };
1652
+ updateSection();
1653
+ }
1654
+
1643
1655
  #showCertificateViewer = async(event: Event): Promise<void> => {
1644
1656
  event.consume();
1645
1657
  const names = await SDK.NetworkManager.MultitargetNetworkManager.instance().getCertificate(this.#origin);
@@ -1667,30 +1679,6 @@ export class SecurityOriginView extends UI.Widget.VBox {
1667
1679
  };
1668
1680
  }
1669
1681
 
1670
- export class SecurityDetailsTable {
1671
- readonly #element: HTMLTableElement;
1672
-
1673
- constructor() {
1674
- this.#element = document.createElement('table');
1675
- this.#element.classList.add('details-table');
1676
- }
1677
-
1678
- element(): HTMLTableElement {
1679
- return this.#element;
1680
- }
1681
-
1682
- addRow(key: string, value: string|Node): void {
1683
- const row = this.#element.createChild('tr', 'details-table-row');
1684
- row.createChild('td').textContent = key;
1685
-
1686
- const valueCell = row.createChild('td');
1687
- if (typeof value === 'string') {
1688
- valueCell.textContent = value;
1689
- } else {
1690
- valueCell.appendChild(value);
1691
- }
1692
- }
1693
- }
1694
1682
  export interface OriginState {
1695
1683
  securityState: Protocol.Security.SecurityState;
1696
1684
  securityDetails: Protocol.Network.SecurityDetails|null;