chrome-devtools-frontend 1.0.1539972 → 1.0.1541552

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 (106) hide show
  1. package/eslint.config.mjs +167 -151
  2. package/front_end/Tests.js +5 -1
  3. package/front_end/core/common/Revealer.ts +5 -0
  4. package/front_end/core/common/Settings.ts +106 -95
  5. package/front_end/core/host/InspectorFrontendHost.ts +10 -10
  6. package/front_end/core/sdk/NetworkManager.ts +16 -11
  7. package/front_end/core/sdk/sdk-meta.ts +0 -35
  8. package/front_end/entrypoints/main/MainImpl.ts +15 -7
  9. package/front_end/entrypoints/shell/shell.ts +1 -0
  10. package/front_end/entrypoints/trace_app/trace_app.ts +1 -0
  11. package/front_end/foundation/README.md +10 -0
  12. package/front_end/foundation/Universe.ts +29 -0
  13. package/front_end/foundation/foundation.ts +7 -0
  14. package/front_end/generated/InspectorBackendCommands.ts +6 -3
  15. package/front_end/generated/SupportedCSSProperties.js +13 -0
  16. package/front_end/generated/protocol.ts +58 -2
  17. package/front_end/models/ai_assistance/BuiltInAi.ts +2 -1
  18. package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +44 -34
  19. package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.snapshot.txt +121 -56
  20. package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.ts +104 -62
  21. package/front_end/models/ai_assistance/performance/AIQueries.ts +56 -2
  22. package/front_end/{panels/issues → models/issues_manager}/IssueAggregator.ts +83 -65
  23. package/front_end/models/issues_manager/issues_manager.ts +2 -0
  24. package/front_end/models/trace/Processor.ts +5 -4
  25. package/front_end/models/trace/insights/types.ts +1 -1
  26. package/front_end/models/trace/types/TraceEvents.ts +1 -1
  27. package/front_end/models/workspace/IgnoreListManager.ts +41 -47
  28. package/front_end/models/workspace/workspace-meta.ts +40 -0
  29. package/front_end/panels/ai_assistance/components/MarkdownRendererWithCodeBlock.ts +1 -1
  30. package/front_end/panels/ai_assistance/components/PerformanceAgentMarkdownRenderer.ts +1 -1
  31. package/front_end/panels/animation/AnimationTimeline.ts +4 -4
  32. package/front_end/panels/animation/AnimationUI.ts +28 -34
  33. package/front_end/panels/elements/ElementsTreeElement.ts +37 -9
  34. package/front_end/panels/elements/LayoutPane.ts +2 -2
  35. package/front_end/panels/elements/components/AdornerManager.ts +9 -9
  36. package/front_end/panels/elements/layoutPane.css +5 -9
  37. package/front_end/panels/event_listeners/EventListenersView.ts +1 -1
  38. package/front_end/panels/explain/components/ConsoleInsight.ts +498 -449
  39. package/front_end/panels/issues/AffectedResourcesView.ts +3 -4
  40. package/front_end/panels/issues/CorsIssueDetailsView.ts +1 -2
  41. package/front_end/panels/issues/IssueView.ts +1 -1
  42. package/front_end/panels/issues/IssuesPane.ts +12 -15
  43. package/front_end/panels/issues/issues.ts +0 -2
  44. package/front_end/panels/network/NetworkDataGridNode.ts +2 -1
  45. package/front_end/panels/network/RequestConditionsDrawer.ts +149 -46
  46. package/front_end/panels/network/RequestTimingView.ts +13 -8
  47. package/front_end/panels/network/network-meta.ts +11 -0
  48. package/front_end/panels/settings/emulation/components/userAgentClientHintsForm.css +1 -1
  49. package/front_end/panels/sources/DebuggerPlugin.ts +1 -1
  50. package/front_end/panels/sources/WatchExpressionsSidebarPane.ts +1 -1
  51. package/front_end/panels/sources/breakpointsView.css +1 -1
  52. package/front_end/panels/sources/sourcesPanel.css +2 -2
  53. package/front_end/panels/timeline/TimelineFlameChartView.ts +3 -3
  54. package/front_end/panels/timeline/TimelinePanel.ts +3 -3
  55. package/front_end/panels/timeline/components/LayoutShiftDetails.ts +16 -10
  56. package/front_end/panels/timeline/components/SidebarSingleInsightSet.ts +2 -0
  57. package/front_end/panels/timeline/overlays/components/EntryLabelOverlay.ts +4 -1
  58. package/front_end/third_party/chromium/README.chromium +1 -1
  59. package/front_end/third_party/puppeteer/README.chromium +2 -2
  60. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/NetworkManager.d.ts.map +1 -1
  61. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/NetworkManager.js +3 -1
  62. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/NetworkManager.js.map +1 -1
  63. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.d.ts +2 -2
  64. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.js +2 -2
  65. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/version.d.ts +1 -1
  66. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/version.js +1 -1
  67. package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.js +4 -4
  68. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/NetworkManager.d.ts.map +1 -1
  69. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/NetworkManager.js +3 -1
  70. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/NetworkManager.js.map +1 -1
  71. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.d.ts +2 -2
  72. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.js +2 -2
  73. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/version.d.ts +1 -1
  74. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/version.js +1 -1
  75. package/front_end/third_party/puppeteer/package/package.json +1 -1
  76. package/front_end/third_party/puppeteer/package/src/cdp/NetworkManager.ts +3 -1
  77. package/front_end/third_party/puppeteer/package/src/revisions.ts +2 -2
  78. package/front_end/third_party/puppeteer/package/src/util/version.ts +1 -1
  79. package/front_end/ui/components/markdown_view/MarkdownView.ts +6 -6
  80. package/front_end/ui/components/snackbars/Snackbars.docs.ts +46 -0
  81. package/front_end/ui/{components/docs/context_menu/basic.ts → legacy/ContextMenu.docs.ts} +58 -25
  82. package/front_end/ui/legacy/UIUtils.ts +2 -1
  83. package/front_end/ui/legacy/components/inline_editor/BezierEditor.ts +1 -1
  84. package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +148 -125
  85. package/front_end/ui/legacy/components/perf_ui/TimelineOverviewPane.ts +3 -3
  86. package/front_end/ui/legacy/components/perf_ui/pieChart.css +1 -1
  87. package/front_end/ui/legacy/components/utils/Linkifier.ts +1 -1
  88. package/front_end/ui/legacy/inspectorCommon.css +3 -2
  89. package/front_end/ui/visual_logging/KnownContextValues.ts +1 -0
  90. package/mcp/mcp.ts +16 -0
  91. package/package.json +2 -1
  92. package/front_end/ui/components/docs/context_menu/basic.html +0 -45
  93. package/front_end/ui/components/docs/linkifier/simple-url.html +0 -25
  94. package/front_end/ui/components/docs/linkifier/simple-url.ts +0 -25
  95. package/front_end/ui/components/docs/panel_feedback/basic.html +0 -25
  96. package/front_end/ui/components/docs/panel_feedback/basic.ts +0 -21
  97. package/front_end/ui/components/docs/panel_feedback/button.html +0 -25
  98. package/front_end/ui/components/docs/panel_feedback/button.ts +0 -19
  99. package/front_end/ui/components/docs/panel_introduction_steps/basic.html +0 -25
  100. package/front_end/ui/components/docs/panel_introduction_steps/basic.ts +0 -28
  101. package/front_end/ui/components/docs/perf_piechart/basic-with-legend.html +0 -20
  102. package/front_end/ui/components/docs/perf_piechart/basic-with-legend.ts +0 -20
  103. package/front_end/ui/components/docs/perf_piechart/basic-without-legend.html +0 -20
  104. package/front_end/ui/components/docs/perf_piechart/basic-without-legend.ts +0 -18
  105. package/front_end/ui/components/docs/snackbars/basic.html +0 -17
  106. package/front_end/ui/components/docs/snackbars/basic.ts +0 -50
@@ -2,23 +2,44 @@
2
2
  // Use of this source code is governed by a BSD-style license that can be
3
3
  // found in the LICENSE file.
4
4
 
5
- import * as FrontendHelpers from '../../../../testing/EnvironmentHelpers.js';
6
- import * as UI from '../../../legacy/legacy.js';
7
- import * as Lit from '../../../lit/lit.js';
8
- import * as ComponentHelpers from '../../helpers/helpers.js';
5
+ import * as Lit from '../lit/lit.js';
6
+
7
+ import {ContextMenu} from './legacy.js';
9
8
 
10
9
  const {html} = Lit;
11
10
 
12
- await ComponentHelpers.ComponentServerSetup.setup();
13
- await FrontendHelpers.initializeGlobalVars();
11
+ export async function render(container: HTMLElement) {
12
+ const style = document.createElement('style');
13
+ style.textContent = `
14
+ #container > div {
15
+ width: var(--sys-size-34);
16
+ padding: var(--sys-size-11);
17
+ display: flex;
18
+ align-items: center;
19
+ flex-wrap: wrap;
20
+ gap: var(--sys-size-5);
21
+ background-color: var(--sys-color-neutral-container);
22
+ border-radius: var(--sys-shape-corner-medium);
23
+ text-align: center;
24
+
25
+ p {
26
+ vertical-align: middle;
27
+ }
28
+ }
29
+ `;
30
+ container.appendChild(style);
31
+
32
+ const menuButtonSection = document.createElement('div');
33
+ const menuButtonHeader = document.createElement('header');
34
+ menuButtonHeader.textContent = 'DevTools menu button (lit-html)';
35
+ container.appendChild(menuButtonHeader);
36
+ container.appendChild(menuButtonSection);
14
37
 
15
- {
16
- const menuButtonSection = document.querySelector('#menu-button') as HTMLElement;
17
38
  Lit.render(
18
39
  html`
19
40
  <devtools-menu-button
20
41
  icon-name="bin"
21
- .populateMenuCall=${(menu: UI.ContextMenu.ContextMenu) => {
42
+ .populateMenuCall=${(menu: ContextMenu.ContextMenu) => {
22
43
  menu.defaultSection().appendItem('Item', () => {
23
44
  alert('Item clicked');
24
45
  }, {jslogContext: 'item'});
@@ -27,15 +48,19 @@ await FrontendHelpers.initializeGlobalVars();
27
48
  ></devtools-menu-button>
28
49
  `,
29
50
  menuButtonSection);
30
- }
31
51
 
32
- {
52
+ const simpleItemsSection = document.createElement('div');
53
+ simpleItemsSection.innerHTML = '<p>Right-click here</p>';
54
+ const simpleItemsHeader = document.createElement('header');
55
+ simpleItemsHeader.textContent = 'Various simple menu items (imperative API)';
56
+ container.appendChild(simpleItemsHeader);
57
+ container.appendChild(simpleItemsSection);
58
+
33
59
  let checked = true;
34
- const simpleItemMenuSection = document.querySelector('#simple-items');
35
- simpleItemMenuSection?.addEventListener('contextmenu', onSimpleMenu.bind(this));
60
+ simpleItemsSection.addEventListener('contextmenu', onSimpleMenu);
36
61
 
37
62
  function onSimpleMenu(event: Event) {
38
- const simpleMenu = new UI.ContextMenu.ContextMenu(event);
63
+ const simpleMenu = new ContextMenu.ContextMenu(event);
39
64
 
40
65
  // Regular item
41
66
  simpleMenu.defaultSection().appendItem('Regular item', () => {
@@ -63,14 +88,18 @@ await FrontendHelpers.initializeGlobalVars();
63
88
 
64
89
  void simpleMenu.show();
65
90
  }
66
- }
67
91
 
68
- {
69
- const customSectionMenuSection = document.querySelector('#custom-section');
70
- customSectionMenuSection?.addEventListener('contextmenu', onCustomSectionMenu.bind(this));
92
+ const customSection = document.createElement('div');
93
+ customSection.innerHTML = '<p>Right-click here</p>';
94
+ const customSectionHeader = document.createElement('header');
95
+ customSectionHeader.textContent = 'Custom sections (imperative API)';
96
+ container.appendChild(customSectionHeader);
97
+ container.appendChild(customSection);
98
+
99
+ customSection.addEventListener('contextmenu', onCustomSectionMenu);
71
100
 
72
101
  function onCustomSectionMenu(event: Event) {
73
- const customSectionMenu = new UI.ContextMenu.ContextMenu(event);
102
+ const customSectionMenu = new ContextMenu.ContextMenu(event);
74
103
 
75
104
  // First custom section
76
105
  const customSection = customSectionMenu.section('Custom section');
@@ -83,14 +112,18 @@ await FrontendHelpers.initializeGlobalVars();
83
112
 
84
113
  void customSectionMenu.show();
85
114
  }
86
- }
87
115
 
88
- {
89
- const subMenuSection = document.querySelector('#sub-menu');
90
- subMenuSection?.addEventListener('contextmenu', onCustomSectionMenu.bind(this));
116
+ const subMenuSection = document.createElement('div');
117
+ subMenuSection.innerHTML = '<p>Right-click here</p>';
118
+ const subMenuHeader = document.createElement('header');
119
+ subMenuHeader.textContent = 'Sub menu (imperative API)';
120
+ container.appendChild(subMenuHeader);
121
+ container.appendChild(subMenuSection);
91
122
 
92
- function onCustomSectionMenu(event: Event) {
93
- const subMenuMenu = new UI.ContextMenu.ContextMenu(event);
123
+ subMenuSection.addEventListener('contextmenu', onSubMenu);
124
+
125
+ function onSubMenu(event: Event) {
126
+ const subMenuMenu = new ContextMenu.ContextMenu(event);
94
127
 
95
128
  const subMenu =
96
129
  subMenuMenu.defaultSection().appendSubMenuItem('Item to open sub menu', /* disabled */ false, 'my-sub-menu');
@@ -1820,7 +1820,8 @@ export const isScrolledToBottom = (element: Element): boolean => {
1820
1820
  return Math.abs(element.scrollTop + element.clientHeight - element.scrollHeight) <= 2;
1821
1821
  };
1822
1822
 
1823
- export function createSVGChild(element: Element, childType: string, className?: string): Element {
1823
+ export function createSVGChild<K extends keyof SVGElementTagNameMap>(
1824
+ element: Element, childType: K, className?: string): SVGElementTagNameMap[K] {
1824
1825
  const child = element.ownerDocument.createElementNS('http://www.w3.org/2000/svg', childType);
1825
1826
  if (className) {
1826
1827
  child.setAttribute('class', className);
@@ -147,7 +147,7 @@ export class BezierEditor extends Common.ObjectWrapper.eventMixin<EventTypes, ty
147
147
  return category;
148
148
  }
149
149
 
150
- private createPresetModifyIcon(parentElement: Element, className: string, drawPath: string): Element {
150
+ private createPresetModifyIcon(parentElement: Element, className: string, drawPath: string): SVGElement {
151
151
  const icon = UI.UIUtils.createSVGChild(parentElement, 'svg', 'bezier-preset-modify ' + className);
152
152
  icon.setAttribute('width', '20');
153
153
  icon.setAttribute('height', '20');
@@ -39,6 +39,7 @@ import * as TextUtils from '../../../../models/text_utils/text_utils.js';
39
39
  import * as uiI18n from '../../../../ui/i18n/i18n.js';
40
40
  import * as IconButton from '../../../components/icon_button/icon_button.js';
41
41
  import * as TextEditor from '../../../components/text_editor/text_editor.js';
42
+ import {Directives, html, render} from '../../../lit/lit.js';
42
43
  import * as VisualLogging from '../../../visual_logging/visual_logging.js';
43
44
  import * as UI from '../../legacy.js';
44
45
  import type * as Components from '../utils/utils.js';
@@ -49,6 +50,7 @@ import objectPropertiesSectionStyles from './objectPropertiesSection.css.js';
49
50
  import objectValueStyles from './objectValue.css.js';
50
51
  import {createSpansForNodeTitle, RemoteObjectPreviewFormatter} from './RemoteObjectPreviewFormatter.js';
51
52
 
53
+ const {ifDefined} = Directives;
52
54
  const UIStrings = {
53
55
  /**
54
56
  * @description Text in Object Properties Section
@@ -318,6 +320,7 @@ class ArrayGroupTreeNode extends ObjectTreeNodeBase {
318
320
  }
319
321
 
320
322
  export class ObjectTreeNode extends ObjectTreeNodeBase {
323
+ #path?: string;
321
324
  constructor(
322
325
  readonly property: SDK.RemoteObject.RemoteObjectProperty,
323
326
  propertiesMode: ObjectPropertiesMode = ObjectPropertiesMode.OWN_AND_INTERNAL_AND_INHERITED,
@@ -334,6 +337,31 @@ export class ObjectTreeNode extends ObjectTreeNodeBase {
334
337
  return this.property.name;
335
338
  }
336
339
 
340
+ get path(): string {
341
+ if (!this.#path) {
342
+ if (this.property.synthetic) {
343
+ this.#path = this.name;
344
+ return this.name;
345
+ }
346
+
347
+ // https://tc39.es/ecma262/#prod-IdentifierName
348
+ const useDotNotation = /^(?:[$_\p{ID_Start}])(?:[$_\u200C\u200D\p{ID_Continue}])*$/u;
349
+ const isInteger = /^(?:0|[1-9]\d*)$/;
350
+
351
+ const parentPath =
352
+ (this.parent instanceof ObjectTreeNode && !this.parent.property.synthetic) ? this.parent.path : '';
353
+
354
+ if (this.property.private || useDotNotation.test(this.name)) {
355
+ this.#path = parentPath ? `${parentPath}.${this.name}` : this.name;
356
+ } else if (isInteger.test(this.name)) {
357
+ this.#path = `${parentPath}[${this.name}]`;
358
+ } else {
359
+ this.#path = `${parentPath}[${JSON.stringify(this.name)}]`;
360
+ }
361
+ }
362
+ return this.#path;
363
+ }
364
+
337
365
  override selfOrParentIfInternal(): ObjectTreeNodeBase {
338
366
  return this.name === '[[Prototype]]' ? (this.parent ?? this) : this;
339
367
  }
@@ -397,7 +425,7 @@ export class ObjectPropertiesSection extends UI.TreeOutline.TreeOutlineInShadow
397
425
  const shadowRoot = UI.UIUtils.createShadowRootWithCoreStyles(titleElement, {cssFile: objectValueStyles});
398
426
  const propertyValue =
399
427
  ObjectPropertiesSection.createPropertyValue(object, /* wasThrown */ false, /* showPreview */ true);
400
- shadowRoot.appendChild(propertyValue.element);
428
+ shadowRoot.appendChild(propertyValue);
401
429
  const objectPropertiesSection = new ObjectPropertiesSection(object, titleElement, linkifier);
402
430
  objectPropertiesSection.editable = false;
403
431
  if (skipProto) {
@@ -477,7 +505,7 @@ export class ObjectPropertiesSection extends UI.TreeOutline.TreeOutlineInShadow
477
505
  }
478
506
 
479
507
  static valueElementForFunctionDescription(description?: string, includePreview?: boolean, defaultName?: string):
480
- Element {
508
+ HTMLElement {
481
509
  const valueElement = document.createElement('span');
482
510
  valueElement.classList.add('object-value-function');
483
511
  description = description || '';
@@ -560,12 +588,11 @@ export class ObjectPropertiesSection extends UI.TreeOutline.TreeOutlineInShadow
560
588
 
561
589
  static createPropertyValueWithCustomSupport(
562
590
  value: SDK.RemoteObject.RemoteObject, wasThrown: boolean, showPreview: boolean,
563
- linkifier?: Components.Linkifier.Linkifier, isSyntheticProperty?: boolean,
564
- variableName?: string): ObjectPropertyValue {
591
+ linkifier?: Components.Linkifier.Linkifier, isSyntheticProperty?: boolean, variableName?: string): HTMLElement {
565
592
  if (value.customPreview()) {
566
593
  const result = (new CustomPreviewComponent(value)).element;
567
594
  result.classList.add('object-properties-section-custom-section');
568
- return new ObjectPropertyValue(result);
595
+ return result;
569
596
  }
570
597
  return ObjectPropertiesSection.createPropertyValue(
571
598
  value, wasThrown, showPreview, linkifier, isSyntheticProperty, variableName);
@@ -600,9 +627,8 @@ export class ObjectPropertiesSection extends UI.TreeOutline.TreeOutlineInShadow
600
627
 
601
628
  static createPropertyValue(
602
629
  value: SDK.RemoteObject.RemoteObject, wasThrown: boolean, showPreview: boolean,
603
- linkifier?: Components.Linkifier.Linkifier, isSyntheticProperty = false,
604
- variableName?: string): ObjectPropertyValue {
605
- let propertyValue;
630
+ linkifier?: Components.Linkifier.Linkifier, isSyntheticProperty = false, variableName?: string): HTMLElement {
631
+ let propertyValue: HTMLElement;
606
632
  const type = value.type;
607
633
  const subtype = value.subtype;
608
634
  const description = value.description || '';
@@ -611,31 +637,31 @@ export class ObjectPropertiesSection extends UI.TreeOutline.TreeOutlineInShadow
611
637
  const rawLocation = value.debuggerModel().createRawLocationByScriptId(
612
638
  value.value.scriptId, value.value.lineNumber, value.value.columnNumber);
613
639
  if (rawLocation && linkifier) {
614
- return new ObjectPropertyValue(linkifier.linkifyRawLocation(rawLocation, Platform.DevToolsPath.EmptyUrlString));
640
+ return linkifier.linkifyRawLocation(rawLocation, Platform.DevToolsPath.EmptyUrlString);
615
641
  }
616
- propertyValue = new ObjectPropertyValue(createUnknownInternalLocationElement());
642
+ propertyValue = createUnknownInternalLocationElement();
617
643
  } else if (type === 'string' && typeof description === 'string') {
618
644
  propertyValue = createStringElement();
619
645
  } else if (type === 'object' && subtype === 'trustedtype') {
620
646
  propertyValue = createTrustedTypeElement();
621
647
  } else if (type === 'function') {
622
- propertyValue = new ObjectPropertyValue(ObjectPropertiesSection.valueElementForFunctionDescription(description));
648
+ propertyValue = ObjectPropertiesSection.valueElementForFunctionDescription(description);
623
649
  } else if (type === 'object' && subtype === 'node' && description) {
624
- propertyValue = new ObjectPropertyValue(createNodeElement());
650
+ propertyValue = createNodeElement();
625
651
  } else {
626
652
  const valueElement = document.createElement('span');
627
653
  valueElement.classList.add('object-value-' + (subtype || type));
628
654
  if (value.preview && showPreview) {
629
655
  const previewFormatter = new RemoteObjectPreviewFormatter();
630
656
  previewFormatter.appendObjectPreview(valueElement, value.preview, false /* isEntry */);
631
- propertyValue = new ObjectPropertyValue(valueElement);
632
- UI.Tooltip.Tooltip.install(propertyValue.element as HTMLElement, description || '');
657
+ propertyValue = valueElement;
658
+ UI.Tooltip.Tooltip.install(propertyValue as HTMLElement, description || '');
633
659
  } else if (description.length > maxRenderableStringLength) {
634
- propertyValue = new ExpandableTextPropertyValue(valueElement, description, EXPANDABLE_MAX_LENGTH);
660
+ propertyValue = new ExpandableTextPropertyValue(valueElement, description, EXPANDABLE_MAX_LENGTH).element;
635
661
  } else {
636
- propertyValue = new ObjectPropertyValue(valueElement);
637
- propertyValue.element.textContent = description;
638
- UI.Tooltip.Tooltip.install(propertyValue.element as HTMLElement, description);
662
+ propertyValue = valueElement;
663
+ propertyValue.textContent = description;
664
+ UI.Tooltip.Tooltip.install(propertyValue as HTMLElement, description);
639
665
  }
640
666
  if (!isSyntheticProperty) {
641
667
  this.appendMemoryIcon(valueElement, value, variableName);
@@ -646,54 +672,53 @@ export class ObjectPropertiesSection extends UI.TreeOutline.TreeOutlineInShadow
646
672
  const wrapperElement = document.createElement('span');
647
673
  wrapperElement.classList.add('error');
648
674
  wrapperElement.classList.add('value');
649
- wrapperElement.appendChild(
650
- uiI18n.getFormatLocalizedString(str_, UIStrings.exceptionS, {PH1: propertyValue.element}));
651
- propertyValue.element = wrapperElement;
675
+ wrapperElement.appendChild(uiI18n.getFormatLocalizedString(str_, UIStrings.exceptionS, {PH1: propertyValue}));
676
+ propertyValue = wrapperElement;
652
677
  }
653
- propertyValue.element.classList.add('value');
678
+ propertyValue.classList.add('value');
654
679
  return propertyValue;
655
680
 
656
- function createUnknownInternalLocationElement(): Element {
681
+ function createUnknownInternalLocationElement(): HTMLElement {
657
682
  const valueElement = document.createElement('span');
658
683
  valueElement.textContent = '<' + i18nString(UIStrings.unknown) + '>';
659
684
  UI.Tooltip.Tooltip.install(valueElement, description || '');
660
685
  return valueElement;
661
686
  }
662
687
 
663
- function createStringElement(): ObjectPropertyValue {
688
+ function createStringElement(): HTMLElement {
664
689
  const valueElement = document.createElement('span');
665
690
  valueElement.classList.add('object-value-string');
666
691
  const text = JSON.stringify(description);
667
- let propertyValue;
692
+ let propertyValue: HTMLElement;
668
693
  if (description.length > maxRenderableStringLength) {
669
- propertyValue = new ExpandableTextPropertyValue(valueElement, text, EXPANDABLE_MAX_LENGTH);
694
+ propertyValue = new ExpandableTextPropertyValue(valueElement, text, EXPANDABLE_MAX_LENGTH).element;
670
695
  } else {
671
696
  UI.UIUtils.createTextChild(valueElement, text);
672
- propertyValue = new ObjectPropertyValue(valueElement);
697
+ propertyValue = valueElement;
673
698
  UI.Tooltip.Tooltip.install(valueElement, description);
674
699
  }
675
700
  return propertyValue;
676
701
  }
677
702
 
678
- function createTrustedTypeElement(): ObjectPropertyValue {
703
+ function createTrustedTypeElement(): HTMLElement {
679
704
  const valueElement = document.createElement('span');
680
705
  valueElement.classList.add('object-value-trustedtype');
681
706
  const text = `${className} "${description}"`;
682
707
  let propertyValue;
683
708
  if (text.length > maxRenderableStringLength) {
684
- propertyValue = new ExpandableTextPropertyValue(valueElement, text, EXPANDABLE_MAX_LENGTH);
709
+ propertyValue = new ExpandableTextPropertyValue(valueElement, text, EXPANDABLE_MAX_LENGTH).element;
685
710
  } else {
686
711
  const contentString = createStringElement();
687
712
  UI.UIUtils.createTextChild(valueElement, `${className} `);
688
- valueElement.appendChild(contentString.element);
689
- propertyValue = new ObjectPropertyValue(valueElement);
713
+ valueElement.appendChild(contentString);
714
+ propertyValue = valueElement;
690
715
  UI.Tooltip.Tooltip.install(valueElement, text);
691
716
  }
692
717
 
693
718
  return propertyValue;
694
719
  }
695
720
 
696
- function createNodeElement(): Element {
721
+ function createNodeElement(): HTMLElement {
697
722
  const valueElement = document.createElement('span');
698
723
  valueElement.classList.add('object-value-node');
699
724
  createSpansForNodeTitle(valueElement, (description));
@@ -831,6 +856,11 @@ export class RootElement extends UI.TreeOutline.TreeElement {
831
856
  this.listItemElement.addEventListener('contextmenu', this.onContextMenu.bind(this), false);
832
857
  }
833
858
 
859
+ override invalidateChildren(): void {
860
+ super.invalidateChildren();
861
+ this.object.removeChildren();
862
+ }
863
+
834
864
  override onexpand(): void {
835
865
  if (this.treeOutline) {
836
866
  this.treeOutline.element.classList.add('expanded');
@@ -896,7 +926,7 @@ export class ObjectPropertyTreeElement extends UI.TreeOutline.TreeElement {
896
926
  readOnly!: boolean;
897
927
  private prompt!: ObjectPropertyPrompt|undefined;
898
928
  private editableDiv!: HTMLElement;
899
- propertyValue?: ObjectPropertyValue;
929
+ propertyValue?: HTMLElement;
900
930
  expandedValueElement?: Element|null;
901
931
  constructor(property: ObjectTreeNode, linkifier?: Components.Linkifier.Linkifier) {
902
932
  // Pass an empty title, the title gets made later in onattach.
@@ -1190,8 +1220,7 @@ export class ObjectPropertyTreeElement extends UI.TreeOutline.TreeElement {
1190
1220
  if (this.property.property.synthetic) {
1191
1221
  this.nameElement.classList.add('synthetic-property');
1192
1222
  }
1193
-
1194
- this.updatePropertyPath();
1223
+ this.nameElement.title = this.property.path;
1195
1224
 
1196
1225
  const isInternalEntries = this.property.property.synthetic && this.property.name === '[[Entries]]';
1197
1226
  if (isInternalEntries) {
@@ -1201,8 +1230,8 @@ export class ObjectPropertyTreeElement extends UI.TreeOutline.TreeElement {
1201
1230
  const showPreview = this.property.name !== '[[Prototype]]';
1202
1231
  this.propertyValue = ObjectPropertiesSection.createPropertyValueWithCustomSupport(
1203
1232
  this.property.object, this.property.property.wasThrown, showPreview, this.linkifier,
1204
- this.property.property.synthetic, this.path() /* variableName */);
1205
- this.valueElement = (this.propertyValue.element as HTMLElement);
1233
+ this.property.property.synthetic, this.property.path /* variableName */);
1234
+ this.valueElement = this.propertyValue;
1206
1235
  } else if (this.property.property.getter) {
1207
1236
  this.valueElement = document.createElement('span');
1208
1237
  const element = this.valueElement.createChild('span');
@@ -1255,37 +1284,7 @@ export class ObjectPropertyTreeElement extends UI.TreeOutline.TreeElement {
1255
1284
  this.listItemElement.appendChild(this.rowContainer);
1256
1285
  }
1257
1286
 
1258
- private updatePropertyPath(): void {
1259
- if (this.nameElement.title) {
1260
- return;
1261
- }
1262
-
1263
- const name = this.property.name;
1264
-
1265
- if (this.property.property.synthetic) {
1266
- UI.Tooltip.Tooltip.install(this.nameElement, name);
1267
- return;
1268
- }
1269
-
1270
- // https://tc39.es/ecma262/#prod-IdentifierName
1271
- const useDotNotation = /^(?:[$_\p{ID_Start}])(?:[$_\u200C\u200D\p{ID_Continue}])*$/u;
1272
- const isInteger = /^(?:0|[1-9]\d*)$/;
1273
-
1274
- const parentPath = (this.parent instanceof ObjectPropertyTreeElement && this.parent.nameElement &&
1275
- !this.parent.property.property.synthetic) ?
1276
- this.parent.nameElement.title :
1277
- '';
1278
-
1279
- if (this.property.property.private || useDotNotation.test(name)) {
1280
- UI.Tooltip.Tooltip.install(this.nameElement, parentPath ? `${parentPath}.${name}` : name);
1281
- } else if (isInteger.test(name)) {
1282
- UI.Tooltip.Tooltip.install(this.nameElement, `${parentPath}[${name}]`);
1283
- } else {
1284
- UI.Tooltip.Tooltip.install(this.nameElement, `${parentPath}[${JSON.stringify(name)}]`);
1285
- }
1286
- }
1287
-
1288
- private contextMenuFired(event: Event): void {
1287
+ getContextMenu(event: Event): UI.ContextMenu.ContextMenu {
1289
1288
  const contextMenu = new UI.ContextMenu.ContextMenu(event);
1290
1289
  contextMenu.appendApplicableItems(this);
1291
1290
  if (this.property.property.symbol) {
@@ -1318,9 +1317,11 @@ export class ObjectPropertyTreeElement extends UI.TreeOutline.TreeElement {
1318
1317
  i18nString(UIStrings.collapseChildren), this.collapseChildren.bind(this),
1319
1318
  {jslogContext: 'collapse-children'});
1320
1319
  }
1321
- if (this.propertyValue) {
1322
- this.propertyValue.appendApplicableItems(event, contextMenu, {});
1323
- }
1320
+ return contextMenu;
1321
+ }
1322
+
1323
+ private contextMenuFired(event: Event): void {
1324
+ const contextMenu = this.getContextMenu(event);
1324
1325
  void contextMenu.show();
1325
1326
  }
1326
1327
 
@@ -1448,6 +1449,11 @@ export class ObjectPropertyTreeElement extends UI.TreeOutline.TreeElement {
1448
1449
  }
1449
1450
  }
1450
1451
 
1452
+ override invalidateChildren(): void {
1453
+ super.invalidateChildren();
1454
+ this.property.removeChildren();
1455
+ }
1456
+
1451
1457
  private onInvokeGetterClick(result: SDK.RemoteObject.CallFunctionResult): void {
1452
1458
  if (!result.object) {
1453
1459
  return;
@@ -1641,6 +1647,11 @@ export class ArrayGroupingTreeElement extends UI.TreeOutline.TreeElement {
1641
1647
  ObjectPropertyTreeElement.populateWithProperties(treeNode, children, false, false, linkifier);
1642
1648
  }
1643
1649
 
1650
+ override invalidateChildren(): void {
1651
+ super.invalidateChildren();
1652
+ this.#child.removeChildren();
1653
+ }
1654
+
1644
1655
  override async onpopulate(): Promise<void> {
1645
1656
  this.removeChildren();
1646
1657
  this.#child.removeChildren();
@@ -1773,74 +1784,86 @@ export class Renderer implements UI.UIUtils.Renderer {
1773
1784
  }
1774
1785
  }
1775
1786
 
1776
- export class ObjectPropertyValue implements UI.ContextMenu.Provider<Object> {
1777
- element: Element;
1778
- constructor(element: Element) {
1779
- this.element = element;
1780
- }
1781
-
1782
- appendApplicableItems(_event: Event, _contextMenu: UI.ContextMenu.ContextMenu, _object: Object): void {
1783
- }
1784
- }
1785
-
1786
- export class ExpandableTextPropertyValue extends ObjectPropertyValue {
1787
+ export class ExpandableTextPropertyValue {
1787
1788
  private readonly text: string;
1788
1789
  private readonly maxLength: number;
1789
- private expandElement: Element|null;
1790
1790
  private readonly maxDisplayableTextLength: number;
1791
- private readonly expandElementText: Common.UIString.LocalizedString|undefined;
1792
- private readonly copyButtonText: Common.UIString.LocalizedString;
1793
- constructor(element: Element, text: string, maxLength: number) {
1794
- // abbreviated text and expandable text controls are added as children to element
1795
- super(element);
1796
- const container = element.createChild('span');
1791
+ readonly #byteCount: number;
1792
+ #expanded = false;
1793
+ #element: HTMLElement;
1794
+
1795
+ constructor(element: HTMLElement, text: string, maxLength: number) {
1796
+ this.#element = element;
1797
1797
  this.text = text;
1798
1798
  this.maxLength = maxLength;
1799
- container.textContent = text.slice(0, maxLength);
1800
- UI.Tooltip.Tooltip.install(container as HTMLElement, `${text.slice(0, maxLength)}…`);
1801
-
1802
- this.expandElement = container.createChild('button');
1803
1799
  this.maxDisplayableTextLength = 10000000;
1800
+ this.#byteCount = Platform.StringUtilities.countWtf8Bytes(text);
1801
+ this.#render();
1802
+ }
1804
1803
 
1805
- const byteCount = Platform.StringUtilities.countWtf8Bytes(text);
1806
- const totalBytesText = i18n.ByteUtilities.bytesToString(byteCount);
1807
- if (this.text.length < this.maxDisplayableTextLength) {
1808
- this.expandElementText = i18nString(UIStrings.showMoreS, {PH1: totalBytesText});
1809
- this.expandElement.setAttribute('data-text', this.expandElementText);
1810
- this.expandElement.setAttribute('jslog', `${VisualLogging.action('expand').track({click: true})}`);
1811
- this.expandElement.classList.add('expandable-inline-button');
1812
- this.expandElement.addEventListener('click', this.expandText.bind(this));
1813
- } else {
1814
- this.expandElement.setAttribute('data-text', i18nString(UIStrings.longTextWasTruncatedS, {PH1: totalBytesText}));
1815
- this.expandElement.classList.add('undisplayable-text');
1816
- }
1817
-
1818
- this.copyButtonText = i18nString(UIStrings.copy);
1819
- const copyButton = container.createChild('button', 'expandable-inline-button');
1820
- copyButton.setAttribute('data-text', this.copyButtonText);
1821
- copyButton.setAttribute('jslog', `${VisualLogging.action('copy').track({click: true})}`);
1822
- copyButton.addEventListener('click', this.copyText.bind(this));
1804
+ get element(): HTMLElement {
1805
+ return this.#element;
1823
1806
  }
1824
1807
 
1825
- override appendApplicableItems(_event: Event, contextMenu: UI.ContextMenu.ContextMenu, _object: Object): void {
1826
- if (this.text.length < this.maxDisplayableTextLength && this.expandElement) {
1808
+ #render(): void {
1809
+ const totalBytesText = i18n.ByteUtilities.bytesToString(this.#byteCount);
1810
+ const onContextMenu = (e: Event): void => {
1811
+ const {target} = e;
1812
+ if (!(target instanceof Element)) {
1813
+ return;
1814
+ }
1815
+ const listItem = target.closest('li');
1816
+ const element = listItem && UI.TreeOutline.TreeElement.getTreeElementBylistItemNode(listItem);
1817
+ if (!(element instanceof ObjectPropertyTreeElement)) {
1818
+ return;
1819
+ }
1820
+ const contextMenu = element.getContextMenu(e);
1821
+ if (this.text.length < this.maxDisplayableTextLength && !this.#expanded) {
1822
+ contextMenu.clipboardSection().appendItem(
1823
+ i18nString(UIStrings.showMoreS, {PH1: totalBytesText}), this.expandText.bind(this),
1824
+ {jslogContext: 'show-more'});
1825
+ }
1827
1826
  contextMenu.clipboardSection().appendItem(
1828
- this.expandElementText || '', this.expandText.bind(this), {jslogContext: 'show-more'});
1829
- }
1830
- contextMenu.clipboardSection().appendItem(this.copyButtonText, this.copyText.bind(this), {jslogContext: 'copy'});
1827
+ i18nString(UIStrings.copy), this.copyText.bind(this), {jslogContext: 'copy'});
1828
+ void contextMenu.show();
1829
+ e.consume(true);
1830
+ };
1831
+
1832
+ const croppedText = this.text.slice(0, this.maxLength);
1833
+
1834
+ // eslint-disable-next-line @devtools/no-lit-render-outside-of-view
1835
+ render(
1836
+ // clang-format off
1837
+ html`<span title=${croppedText + '…'} @contextmenu=${onContextMenu}>
1838
+ ${this.#expanded ? this.text : croppedText}
1839
+ <button
1840
+ ?hidden=${this.#expanded}
1841
+ @click=${this.#canExpand ? this.expandText.bind(this) : undefined}
1842
+ jslog=${ifDefined(this.#canExpand ? VisualLogging.action('expand').track({click: true}) : undefined)}
1843
+ class=${this.#canExpand ? 'expandable-inline-button' : 'undisplayable-text'}
1844
+ data-text=${this.#canExpand ? i18nString(UIStrings.showMoreS, {PH1: totalBytesText}) :
1845
+ i18nString(UIStrings.longTextWasTruncatedS, {PH1: totalBytesText})}
1846
+ ></button>
1847
+ <button
1848
+ class=expandable-inline-button
1849
+ @click=${this.copyText.bind(this)}
1850
+ data-text=${i18nString(UIStrings.copy)}
1851
+ jslog=${VisualLogging.action('copy').track({click: true})}
1852
+ ></button>
1853
+ </span>`,
1854
+ // clang-format on
1855
+ this.#element);
1856
+ }
1857
+
1858
+ get #canExpand(): boolean {
1859
+ return this.text.length < this.maxDisplayableTextLength;
1831
1860
  }
1832
1861
 
1833
1862
  private expandText(): void {
1834
- if (!this.expandElement) {
1835
- return;
1836
- }
1837
-
1838
- if (this.expandElement.parentElement) {
1839
- this.expandElement.parentElement.insertBefore(
1840
- document.createTextNode(this.text.slice(this.maxLength)), this.expandElement);
1863
+ if (!this.#expanded) {
1864
+ this.#expanded = true;
1865
+ this.#render();
1841
1866
  }
1842
- this.expandElement.remove();
1843
- this.expandElement = null;
1844
1867
  }
1845
1868
 
1846
1869
  private copyText(): void {
@@ -337,7 +337,7 @@ export class TimelineOverviewPane extends Common.ObjectWrapper.eventMixin<EventT
337
337
  #initializeDimHighlightSVG(): void {
338
338
  // Set up the desaturation mask
339
339
  const defs = UI.UIUtils.createSVGChild(this.#dimHighlightSVG, 'defs');
340
- const mask = UI.UIUtils.createSVGChild(defs, 'mask') as SVGMaskElement;
340
+ const mask = UI.UIUtils.createSVGChild(defs, 'mask');
341
341
  mask.id = 'dim-highlight-cutouts';
342
342
  /* Within the mask...
343
343
  - black fill = punch, fully transparently, through to the next thing. these are the cutouts to the color.
@@ -357,7 +357,7 @@ export class TimelineOverviewPane extends Common.ObjectWrapper.eventMixin<EventT
357
357
  // `mask` element.
358
358
  // The `mixBlendMode` is set to 'saturation', so this rectangle will completely desaturate the area it covers
359
359
  // within the mask.
360
- const desaturateRect = UI.UIUtils.createSVGChild(this.#dimHighlightSVG, 'rect', 'background') as SVGRectElement;
360
+ const desaturateRect = UI.UIUtils.createSVGChild(this.#dimHighlightSVG, 'rect', 'background');
361
361
  desaturateRect.setAttribute('width', '100%');
362
362
  desaturateRect.setAttribute('height', '100%');
363
363
  desaturateRect.setAttribute('fill', ThemeSupport.ThemeSupport.instance().getComputedValue('--color-background'));
@@ -374,7 +374,7 @@ export class TimelineOverviewPane extends Common.ObjectWrapper.eventMixin<EventT
374
374
 
375
375
  // This polygon is for the bracket beyond the not desaturated area.
376
376
  const bracketColor = ThemeSupport.ThemeSupport.instance().getComputedValue('--sys-color-state-on-header-hover');
377
- const bracket = UI.UIUtils.createSVGChild(this.#dimHighlightSVG, 'polygon') as SVGRectElement;
377
+ const bracket = UI.UIUtils.createSVGChild(this.#dimHighlightSVG, 'polygon');
378
378
  bracket.setAttribute('fill', bracketColor);
379
379
 
380
380
  ThemeSupport.ThemeSupport.instance().addEventListener(ThemeSupport.ThemeChangeEvent.eventName, () => {
@@ -33,7 +33,7 @@
33
33
  }
34
34
 
35
35
  :focus {
36
- outline-width: 0;
36
+ outline-style: none;
37
37
  }
38
38
 
39
39
  .pie-chart-total.selected {
@@ -320,7 +320,7 @@ export class Linkifier extends Common.ObjectWrapper.ObjectWrapper<EventTypes> im
320
320
 
321
321
  linkifyRawLocation(
322
322
  rawLocation: SDK.DebuggerModel.Location, fallbackUrl: Platform.DevToolsPath.UrlString,
323
- className?: string): Element {
323
+ className?: string): HTMLElement {
324
324
  return this.linkifyScriptLocation(
325
325
  rawLocation.debuggerModel.target(), rawLocation.scriptId, fallbackUrl, rawLocation.lineNumber, {
326
326
  columnNumber: rawLocation.columnNumber,