chrome-devtools-frontend 1.0.1674545 → 1.0.1675430

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 (45) hide show
  1. package/.agents/skills/devtools-ux-writing-refactor/SKILL.md +26 -9
  2. package/front_end/core/common/ParsedURL.ts +5 -1
  3. package/front_end/core/common/SettingRegistration.ts +29 -8
  4. package/front_end/core/common/Settings.ts +0 -11
  5. package/front_end/core/root/Runtime.ts +4 -0
  6. package/front_end/core/sdk/TargetManager.ts +18 -0
  7. package/front_end/entrypoints/main/MainImpl.ts +1 -0
  8. package/front_end/foundation/Universe.ts +44 -29
  9. package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +15 -5
  10. package/front_end/models/emulation/DeviceModeModel.ts +60 -71
  11. package/front_end/models/emulation/EmulatedDevices.ts +18 -66
  12. package/front_end/models/persistence/AutomaticFileSystemManager.ts +2 -2
  13. package/front_end/models/persistence/IsolatedFileSystemManager.ts +20 -29
  14. package/front_end/models/workspace/FileManager.ts +13 -6
  15. package/front_end/panels/application/WebMCPView.ts +13 -5
  16. package/front_end/panels/application/components/AdsView.ts +24 -1
  17. package/front_end/panels/application/components/adsView.css +27 -21
  18. package/front_end/panels/autofill/AutofillView.ts +12 -12
  19. package/front_end/panels/autofill/autofill-meta.ts +2 -2
  20. package/front_end/panels/elements/ElementsTreeElement.ts +550 -327
  21. package/front_end/panels/elements/ElementsTreeOutline.ts +1 -1
  22. package/front_end/panels/js_timeline/js_timeline-meta.ts +8 -8
  23. package/front_end/panels/layer_viewer/LayerDetailsView.ts +30 -30
  24. package/front_end/panels/layer_viewer/LayerTreeOutline.ts +6 -6
  25. package/front_end/panels/layer_viewer/LayerViewHost.ts +1 -1
  26. package/front_end/panels/layer_viewer/Layers3DView.ts +11 -11
  27. package/front_end/panels/layer_viewer/PaintProfilerView.ts +8 -8
  28. package/front_end/panels/layer_viewer/TransformController.ts +3 -3
  29. package/front_end/panels/layer_viewer/layer_viewer-meta.ts +11 -11
  30. package/front_end/panels/mobile_throttling/ThrottlingManager.ts +1 -58
  31. package/front_end/panels/mobile_throttling/mobile_throttling.ts +0 -10
  32. package/front_end/panels/protocol_monitor/ProtocolMonitor.ts +7 -0
  33. package/front_end/panels/settings/emulation/DevicesSettingsTab.ts +1 -2
  34. package/front_end/third_party/chromium/README.chromium +1 -1
  35. package/front_end/ui/comments/CommentAnchorResolver.ts +132 -0
  36. package/front_end/ui/comments/comments.ts +9 -0
  37. package/front_end/ui/legacy/components/data_grid/DataGridElement.ts +40 -0
  38. package/front_end/ui/legacy/components/utils/Linkifier.ts +1 -0
  39. package/front_end/ui/settings/SettingUIRegistration.ts +4 -0
  40. package/front_end/ui/visual_logging/Debugging.ts +1 -5
  41. package/front_end/ui/visual_logging/KnownContextValues.ts +1 -0
  42. package/front_end/ui/visual_logging/LoggingConfig.ts +21 -0
  43. package/front_end/ui/visual_logging/visual_logging.ts +9 -0
  44. package/package.json +1 -1
  45. package/front_end/panels/mobile_throttling/MobileThrottlingSelector.ts +0 -92
@@ -2,6 +2,7 @@
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
  /* eslint-disable @devtools/no-lit-render-outside-of-view */
5
+ /* eslint-disable @devtools/no-imperative-dom-api */
5
6
 
6
7
  /*
7
8
  * Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
@@ -574,7 +575,7 @@ function renderTitle(
574
575
  return openingTag;
575
576
  }
576
577
 
577
- if (ElementsTreeElement.canShowInlineText(node)) {
578
+ if (ElementsTreeWidget.canShowInlineText(node)) {
578
579
  const firstChild = node.firstChild;
579
580
  if (!firstChild) {
580
581
  throw new Error('ElementsTreeElement._nodeTitleInfo expects node.firstChild to be defined.');
@@ -761,6 +762,7 @@ function renderLinkifiedValue(value: string, node: SDK.DOMModel.DOMNode): Lit.Te
761
762
  text: value,
762
763
  preventClick: true,
763
764
  showColumnNumber: false,
765
+ bypassURLTrimming: true,
764
766
  onRef: link => {
765
767
  ImagePreviewPopover.setImageUrl(link, rewrittenHref);
766
768
  },
@@ -1236,16 +1238,16 @@ export const DEFAULT_VIEW = (input: ViewInput, output: ViewOutput, target: HTMLE
1236
1238
  // clang-format on
1237
1239
  };
1238
1240
 
1239
- export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
1241
+ export class ElementsTreeWidget extends UI.Widget.Widget {
1242
+ treeElement: ElementsTreeElement&{treeOutline: ElementsTreeOutline | null};
1240
1243
  nodeInternal: SDK.DOMModel.DOMNode;
1241
- override treeOutline: ElementsTreeOutline|null;
1242
1244
 
1243
1245
  private searchQuery: string|null;
1244
1246
  #expandedChildrenLimit: number;
1245
1247
  private readonly decorationsThrottler: Common.Throttler.Throttler;
1246
1248
  private inClipboard: boolean;
1247
1249
  #hovered: boolean;
1248
- private editing: EditorHandles|null;
1250
+ editing: EditorHandles|null;
1249
1251
  #editorRef?: TextEditor.TextEditor.TextEditor;
1250
1252
  // True while the Edit as HTML editor's own context menu is open, so that the
1251
1253
  // focusout caused by the menu taking focus does not commit the edit.
@@ -1257,8 +1259,6 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
1257
1259
  #nodeElementToIssue = new Map<Element, IssuesManager.Issue.Issue[]>();
1258
1260
  #highlights: Range[] = [];
1259
1261
 
1260
- readonly tagTypeContext: TagTypeContext;
1261
-
1262
1262
  #adornersThrottler = new Common.Throttler.Throttler(100);
1263
1263
  #containerAdornerActive = false;
1264
1264
  #flexAdornerActive = false;
@@ -1275,26 +1275,20 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
1275
1275
  #decorationsTooltip = '';
1276
1276
 
1277
1277
  static #adTooltipIdCounter = 0;
1278
- #adTooltipId = `ad-tooltip-${++ElementsTreeElement.#adTooltipIdCounter}`;
1278
+ #adTooltipId = `ad-tooltip-${++ElementsTreeWidget.#adTooltipIdCounter}`;
1279
1279
 
1280
1280
  #updateRecord: Elements.ElementUpdateRecord.ElementUpdateRecord|null = null;
1281
1281
 
1282
1282
  // Used to add the content to TreeElement's title element.
1283
1283
  // Relied on by web tests.
1284
- #contentElement?: HTMLElement;
1285
1284
 
1286
- constructor(node: SDK.DOMModel.DOMNode, isClosingTag?: boolean) {
1287
- // The title will be updated in onattach.
1285
+ constructor(treeElement: ElementsTreeElement) {
1288
1286
  super();
1287
+ this.treeElement = treeElement as ElementsTreeElement & {treeOutline: ElementsTreeOutline | null};
1288
+ const node = treeElement.node();
1289
+ const isClosingTag = treeElement.isClosingTag();
1289
1290
  this.nodeInternal = node;
1290
- this.treeOutline = null;
1291
- this.listItemElement.setAttribute(
1292
- 'jslog', `${VisualLogging.treeItem().parent('elementsTreeOutline').track({
1293
- keydown: 'ArrowUp|ArrowDown|ArrowLeft|ArrowRight|Backspace|Delete|Enter|Space|Home|End',
1294
- resize: true,
1295
- drag: true,
1296
- click: true,
1297
- })}`);
1291
+ this.treeElement.treeOutline = null;
1298
1292
 
1299
1293
  this.searchQuery = null;
1300
1294
  this.#expandedChildrenLimit = InitialChildrenLimit;
@@ -1305,30 +1299,10 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
1305
1299
 
1306
1300
  this.editing = null;
1307
1301
 
1308
- if (isClosingTag) {
1309
- this.tagTypeContext = {tagType: TagType.CLOSING};
1310
- } else {
1311
- this.tagTypeContext = {
1312
- tagType: TagType.OPENING,
1313
- canAddAttributes: this.nodeInternal.nodeType() === Node.ELEMENT_NODE,
1314
- };
1302
+ if (!isClosingTag) {
1315
1303
  void this.#updateAdorners();
1316
1304
  }
1317
1305
  this.expandAllButtonElement = null;
1318
- this.performUpdate();
1319
-
1320
- if (this.nodeInternal.retained && !this.isClosingTag()) {
1321
- this.setLeadingIcons([
1322
- html`<devtools-icon class="extra-small" name="small-status-dot" style="color:var(--icon-error); vertical-align:middle"></devtools-icon>`,
1323
- ]);
1324
- this.listItemNode.classList.add('detached-elements-detached-node');
1325
- this.listItemNode.style.setProperty('display', '-webkit-box');
1326
- this.listItemNode.setAttribute('title', 'Retained Node');
1327
- }
1328
-
1329
- if (this.nodeInternal.detached && !this.isClosingTag()) {
1330
- this.listItemNode.setAttribute('title', 'Detached Tree Node');
1331
- }
1332
1306
  }
1333
1307
 
1334
1308
  static animateOnDOMUpdate(treeElement: ElementsTreeElement): void {
@@ -1349,7 +1323,7 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
1349
1323
  }
1350
1324
 
1351
1325
  static canShowInlineText(node: SDK.DOMModel.DOMNode): boolean {
1352
- if (node.contentDocument() || node.templateContent() || ElementsTreeElement.visibleShadowRoots(node).length ||
1326
+ if (node.contentDocument() || node.templateContent() || ElementsTreeWidget.visibleShadowRoots(node).length ||
1353
1327
  node.hasPseudoElements()) {
1354
1328
  return false;
1355
1329
  }
@@ -1384,65 +1358,72 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
1384
1358
  }
1385
1359
  }
1386
1360
 
1387
- // ClearNode param is used to clean DOM after in-place editing..
1388
- performUpdate(clearNode = false): void {
1361
+ #clearDOMNextUpdate = false;
1362
+
1363
+ override performUpdate(): void {
1389
1364
  // Skip updating when in-place editing (not HTML editing indicated by the
1390
1365
  // editorState) is happening. Doing an update would break editing
1391
1366
  // (crbug.com/515639787).
1392
1367
  if (this.editing && !this.#editorState) {
1393
1368
  return;
1394
1369
  }
1370
+ if (this.#clearDOMNextUpdate) {
1371
+ this.#clearDOMNextUpdate = false;
1372
+ Lit.render(Lit.nothing, this.contentElement, {host: this});
1373
+ }
1374
+ const isClosingTag = this.treeElement.isClosingTag();
1395
1375
  const output: ViewOutput = {};
1396
1376
  DEFAULT_VIEW({
1397
- node: !clearNode ? this.nodeInternal : null,
1398
- isClosingTag: this.isClosingTag(),
1399
- expanded: this.expanded,
1400
- isExpandable: this.isExpandable(),
1401
- isXMLMimeType: Boolean(this.treeOutline?.isXMLMimeType),
1377
+ node: this.nodeInternal,
1378
+ isClosingTag,
1379
+ expanded: this.treeElement.expanded,
1380
+ isExpandable: this.treeElement.isExpandable(),
1381
+ isXMLMimeType: Boolean(this.treeElement.treeOutline?.isXMLMimeType),
1402
1382
  updateRecord: this.#updateRecord,
1403
1383
  onHighlightSearchResults: () => this.#highlightSearchResults(),
1404
- onExpand: () => this.expand(),
1384
+ onExpand: () => this.treeElement.expand(),
1405
1385
 
1406
1386
  containerAdornerActive: this.#containerAdornerActive,
1407
1387
  adProvenance: this.nodeInternal.adProvenance(),
1408
1388
  adTooltipId: this.#adTooltipId,
1409
1389
  target: this.nodeInternal.domModel().target(),
1410
- showContainerAdorner: Boolean(this.#layout?.containerType) && !this.isClosingTag(),
1390
+ showContainerAdorner: Boolean(this.#layout?.containerType) && !isClosingTag,
1411
1391
  containerType: this.#layout?.containerType,
1412
- showFlexAdorner: Boolean(this.#layout?.isFlex) && !this.isClosingTag(),
1392
+ showFlexAdorner: Boolean(this.#layout?.isFlex) && !isClosingTag,
1413
1393
  flexAdornerActive: this.#flexAdornerActive,
1414
- showGridAdorner: Boolean(this.#layout?.isGrid) && !this.isClosingTag(),
1415
- showGridLanesAdorner: Boolean(this.#layout?.isGridLanes) && !this.isClosingTag(),
1416
- showMediaAdorner: this.node().isMediaNode() && !this.isClosingTag(),
1417
- showPopoverAdorner:
1418
- Boolean(this.node().attributes().find(attr => attr.name === 'popover')) && !this.isClosingTag(),
1394
+ showGridAdorner: Boolean(this.#layout?.isGrid) && !isClosingTag,
1395
+ showGridLanesAdorner: Boolean(this.#layout?.isGridLanes) && !isClosingTag,
1396
+ showMediaAdorner: this.node().isMediaNode() && !isClosingTag,
1397
+ showPopoverAdorner: Boolean(this.node().attributes().find(attr => attr.name === 'popover')) && !isClosingTag,
1419
1398
  showInterestAdorner: Boolean(Root.Runtime.hostConfig.devToolsAllowInterestForcing?.enabled) &&
1420
1399
  Boolean(this.node().attributes().find(attr =>
1421
1400
  attr.name === 'interesttarget' || attr.name === 'interestfor')) &&
1422
- !this.isClosingTag(),
1423
- showTopLayerAdorner: this.node().topLayerIndex() !== -1 && !this.isClosingTag(),
1401
+ !isClosingTag,
1402
+ showTopLayerAdorner: this.node().topLayerIndex() !== -1 && !isClosingTag,
1424
1403
  gridAdornerActive: this.#gridAdornerActive,
1425
1404
  popoverAdornerActive: this.#popoverAdornerActive,
1426
1405
  interestAdornerActive: this.#interestAdornerActive,
1427
1406
  isSubgrid: Boolean(this.#layout?.isSubgrid),
1428
- showViewSourceAdorner: this.nodeInternal.isRootNode() && isOpeningTag(this.tagTypeContext),
1407
+ showViewSourceAdorner: this.nodeInternal.isRootNode() && isOpeningTag(this.treeElement.tagTypeContext),
1429
1408
  showScrollAdorner: ((this.node().nodeName() === 'HTML' && this.node().ownerDocument?.isScrollable()) ||
1430
1409
  (this.node().nodeName() !== '#document' && this.node().isScrollable())) &&
1431
- !this.isClosingTag(),
1410
+ !isClosingTag,
1432
1411
  decorations: this.#decorations,
1433
- descendantDecorations: this.expanded ? [] : this.#descendantDecorations,
1412
+ descendantDecorations: this.treeElement.expanded ? [] : this.#descendantDecorations,
1434
1413
  decorationsTooltip: this.#decorationsTooltip,
1435
1414
  indent: this.computeLeftIndent(),
1436
- showScrollSnapAdorner: Boolean(this.#layout?.hasScroll) && !this.isClosingTag(),
1415
+ showScrollSnapAdorner: Boolean(this.#layout?.hasScroll) && !isClosingTag,
1437
1416
  scrollSnapAdornerActive: this.#scrollSnapAdornerActive,
1438
- showSlotAdorner: Boolean(this.nodeInternal.assignedSlot) && !this.isClosingTag(),
1439
- showCustomElementAdorner: this.node().isCustomElement() && !this.isClosingTag(),
1440
- onCustomElementAdornerClick:
1441
- this.treeOutline?.disableEdits ? () => {} : (event: Event) => void this.#onCustomElementAdornerClick(event),
1442
- showStartingStyleAdorner: this.nodeInternal.affectedByStartingStyles() && !this.isClosingTag(),
1417
+ showSlotAdorner: Boolean(this.nodeInternal.assignedSlot) && !isClosingTag,
1418
+ showCustomElementAdorner: this.node().isCustomElement() && !isClosingTag,
1419
+ onCustomElementAdornerClick: this.treeElement.treeOutline?.disableEdits ?
1420
+ () => {} :
1421
+ (event: Event) => void this.#onCustomElementAdornerClick(event),
1422
+ showStartingStyleAdorner: this.nodeInternal.affectedByStartingStyles() && !isClosingTag,
1443
1423
  startingStyleAdornerActive: this.#startingStyleAdornerActive,
1444
- onStartingStyleAdornerClick:
1445
- this.treeOutline?.disableEdits ? () => {} : (event: Event) => this.#onStartingStyleAdornerClick(event),
1424
+ onStartingStyleAdornerClick: this.treeElement.treeOutline?.disableEdits ?
1425
+ () => {} :
1426
+ (event: Event) => this.#onStartingStyleAdornerClick(event),
1446
1427
  onSlotAdornerClick:
1447
1428
  () => {
1448
1429
  if (this.nodeInternal.assignedSlot) {
@@ -1453,38 +1434,45 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
1453
1434
  }
1454
1435
  },
1455
1436
  topLayerIndex: this.node().topLayerIndex(),
1456
- onViewSourceAdornerClick: this.treeOutline?.disableEdits ? () => {} : this.revealHTMLInSources.bind(this),
1437
+ onViewSourceAdornerClick: this.treeElement.treeOutline?.disableEdits ? () => {} :
1438
+ this.revealHTMLInSources.bind(this),
1457
1439
  onGutterClick: this.showContextMenu.bind(this),
1458
- onContainerAdornerClick: this.treeOutline?.disableEdits ? () => {} :
1459
- (event: Event) => this.#onContainerAdornerClick(event),
1460
- onFlexAdornerClick: this.treeOutline?.disableEdits ? () => {} : (event: Event) => this.#onFlexAdornerClick(event),
1461
- onGridAdornerClick: this.treeOutline?.disableEdits ? () => {} : (event: Event) => this.#onGridAdornerClick(event),
1462
- onMediaAdornerClick: this.treeOutline?.disableEdits ? () => {} :
1463
- (event: Event) => this.#onMediaAdornerClick(event),
1464
- onPopoverAdornerClick: this.treeOutline?.disableEdits ? () => {} :
1465
- (event: Event) => this.#onPopoverAdornerClick(event),
1466
- onInterestAdornerClick: this.treeOutline?.disableEdits ? () => {} :
1467
- (event: Event) => this.#onInterestAdornerClick(event),
1468
- onScrollSnapAdornerClick:
1469
- this.treeOutline?.disableEdits ? () => {} : (event: Event) => this.#onScrollSnapAdornerClick(event),
1470
- onTopLayerAdornerClick: this.treeOutline?.disableEdits ? () => {} :
1471
- () => {
1472
- if (!this.treeOutline) {
1473
- return;
1474
- }
1475
- this.treeOutline.revealInTopLayer(this.node());
1476
- },
1440
+ onContainerAdornerClick: this.treeElement.treeOutline?.disableEdits ?
1441
+ () => {} :
1442
+ (event: Event) => this.#onContainerAdornerClick(event),
1443
+ onFlexAdornerClick:
1444
+ this.treeElement.treeOutline?.disableEdits ? () => {} : (event: Event) => this.#onFlexAdornerClick(event),
1445
+ onGridAdornerClick:
1446
+ this.treeElement.treeOutline?.disableEdits ? () => {} : (event: Event) => this.#onGridAdornerClick(event),
1447
+ onMediaAdornerClick:
1448
+ this.treeElement.treeOutline?.disableEdits ? () => {} : (event: Event) => this.#onMediaAdornerClick(event),
1449
+ onPopoverAdornerClick:
1450
+ this.treeElement.treeOutline?.disableEdits ? () => {} : (event: Event) => this.#onPopoverAdornerClick(event),
1451
+ onInterestAdornerClick:
1452
+ this.treeElement.treeOutline?.disableEdits ? () => {} : (event: Event) => this.#onInterestAdornerClick(event),
1453
+ onScrollSnapAdornerClick: this.treeElement.treeOutline?.disableEdits ?
1454
+ () => {} :
1455
+ (event: Event) => this.#onScrollSnapAdornerClick(event),
1456
+ onTopLayerAdornerClick: this.treeElement.treeOutline?.disableEdits ?
1457
+ () => {} :
1458
+ () => {
1459
+ if (!this.treeElement.treeOutline) {
1460
+ return;
1461
+ }
1462
+ this.treeElement.treeOutline.revealInTopLayer(this.node());
1463
+ },
1477
1464
  isHovered: this.#hovered,
1478
- isSelected: this.selected,
1465
+ isSelected: this.treeElement.selected,
1479
1466
  canInspect: this.node().canInspectNode(),
1480
- showAiButton: Boolean(this.#hovered || this.selected) && this.node().nodeType() === Node.ELEMENT_NODE &&
1481
- this.isAiButtonEnabled() && (this.treeOutline as ElementsTreeOutline)?.showAIButton,
1467
+ showAiButton: Boolean(this.#hovered || this.treeElement.selected) &&
1468
+ this.node().nodeType() === Node.ELEMENT_NODE && this.isAiButtonEnabled() &&
1469
+ (this.treeElement.treeOutline as ElementsTreeOutline)?.showAIButton,
1482
1470
  aiButtonTitle: this.isAiButtonEnabled() ?
1483
1471
  UI.ActionRegistry.ActionRegistry.instance().getAction('freestyler.elements-floating-button').title() :
1484
1472
  undefined,
1485
1473
  onAiButtonClick: (ev: Event) => {
1486
1474
  ev.stopPropagation();
1487
- this.select(true, false);
1475
+ this.treeElement.select(true, false);
1488
1476
  const action = UI.ActionRegistry.ActionRegistry.instance().getAction('freestyler.elements-floating-button');
1489
1477
  if (action) {
1490
1478
  void action.execute();
@@ -1493,15 +1481,52 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
1493
1481
  editorState: this.#editorState,
1494
1482
  editorWidth: this.#editorWidth,
1495
1483
  },
1496
- output, this.listItemElement);
1484
+ output, this.contentElement);
1497
1485
 
1498
- this.#contentElement = output.contentElement;
1499
1486
  this.#editorRef = output.editorRef;
1500
1487
  if (this.#updateRecord) {
1501
1488
  this.#updateRecord = null;
1502
1489
  }
1503
1490
  }
1504
1491
 
1492
+ async #onCustomElementAdornerClick(event: Event): Promise<void> {
1493
+ event.stopPropagation();
1494
+ const node = this.node();
1495
+ const object = await node.resolveToObject('');
1496
+ if (!object) {
1497
+ return;
1498
+ }
1499
+ let constructorObject: SDK.RemoteObject.RemoteObject|null = null;
1500
+ try {
1501
+ const result = await object.callFunction(function(this: Element): unknown {
1502
+ const selector = this.getAttribute('is') || this.tagName.toLowerCase();
1503
+ return (typeof customElements !== 'undefined' && customElements.get(selector)) || this.constructor;
1504
+ });
1505
+ constructorObject = result.object;
1506
+ } finally {
1507
+ object.release();
1508
+ }
1509
+ if (!constructorObject) {
1510
+ return;
1511
+ }
1512
+ try {
1513
+ if (constructorObject.type === 'function') {
1514
+ const functionDetails =
1515
+ await SDK.RemoteObject.RemoteFunction.objectAsFunction(constructorObject).targetFunctionDetails();
1516
+ if (functionDetails?.location) {
1517
+ const uiLocation =
1518
+ await Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding.instance().rawLocationToUILocation(
1519
+ functionDetails.location);
1520
+ if (uiLocation) {
1521
+ void Common.Revealer.reveal(uiLocation);
1522
+ }
1523
+ }
1524
+ }
1525
+ } finally {
1526
+ constructorObject.release();
1527
+ }
1528
+ }
1529
+
1505
1530
  #onContainerAdornerClick(event: Event): void {
1506
1531
  event.stopPropagation();
1507
1532
  const node = this.node();
@@ -1576,14 +1601,14 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
1576
1601
 
1577
1602
  highlightAttribute(attributeName: string): void {
1578
1603
  // If the attribute is not found, we highlight the tag name instead.
1579
- let animationElement = this.listItemElement.querySelector('.webkit-html-tag-name') ?? this.listItemElement;
1604
+ let animationElement = this.contentElement.querySelector('.webkit-html-tag-name') ?? this.element;
1580
1605
 
1581
1606
  if (this.nodeInternal.getAttribute(attributeName) !== undefined) {
1582
- const tag = this.listItemElement.getElementsByClassName('webkit-html-tag')[0];
1583
- const attributes = tag.getElementsByClassName('webkit-html-attribute');
1607
+ const tag = this.contentElement.querySelector('.webkit-html-tag');
1608
+ const attributes = tag?.getElementsByClassName('webkit-html-attribute') ?? [];
1584
1609
  for (const attribute of attributes) {
1585
1610
  const attributeElement = attribute.getElementsByClassName('webkit-html-attribute-name')[0];
1586
- if (attributeElement.textContent === attributeName) {
1611
+ if (attributeElement?.textContent === attributeName) {
1587
1612
  animationElement = attributeElement;
1588
1613
  break;
1589
1614
  }
@@ -1592,10 +1617,6 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
1592
1617
  UI.UIUtils.runCSSAnimationOnce(animationElement, DOM_UPDATE_ANIMATION_CLASS_NAME);
1593
1618
  }
1594
1619
 
1595
- isClosingTag(): boolean {
1596
- return !isOpeningTag(this.tagTypeContext);
1597
- }
1598
-
1599
1620
  isDisplayContents(): boolean {
1600
1621
  return Boolean(this.#layout?.isContents);
1601
1622
  }
@@ -1604,7 +1625,7 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
1604
1625
  return this.nodeInternal;
1605
1626
  }
1606
1627
 
1607
- isEditing(): boolean {
1628
+ get isEditing(): boolean {
1608
1629
  return Boolean(this.editing);
1609
1630
  }
1610
1631
 
@@ -1625,7 +1646,7 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
1625
1646
  return;
1626
1647
  }
1627
1648
  this.inClipboard = inClipboard;
1628
- this.listItemElement.classList.toggle('in-clipboard', inClipboard);
1649
+ this.requestUpdate();
1629
1650
  }
1630
1651
 
1631
1652
  get hovered(): boolean {
@@ -1638,15 +1659,7 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
1638
1659
  }
1639
1660
 
1640
1661
  this.#hovered = isHovered;
1641
-
1642
- if (this.listItemElement) {
1643
- if (isHovered) {
1644
- this.listItemElement.classList.add('hovered');
1645
- } else {
1646
- this.listItemElement.classList.remove('hovered');
1647
- }
1648
- this.performUpdate();
1649
- }
1662
+ this.requestUpdate();
1650
1663
  }
1651
1664
 
1652
1665
  addIssue(newIssue: IssuesManager.Issue.Issue): void {
@@ -1676,7 +1689,7 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
1676
1689
  }
1677
1690
 
1678
1691
  #highlightViolatingAttr(name: string, issue: IssuesManager.Issue.Issue): void {
1679
- const tag = this.listItemElement.getElementsByClassName('webkit-html-tag')[0];
1692
+ const tag = this.contentElement.querySelectorAll('.webkit-html-tag')[0];
1680
1693
  const attributes = tag.getElementsByClassName('webkit-html-attribute');
1681
1694
  for (const attribute of attributes) {
1682
1695
  if (attribute.getElementsByClassName('webkit-html-attribute-name')[0].textContent === name) {
@@ -1688,7 +1701,7 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
1688
1701
  }
1689
1702
 
1690
1703
  #highlightTagAsViolating(issue: IssuesManager.Issue.Issue): void {
1691
- const tagElement = this.listItemElement.getElementsByClassName('webkit-html-tag-name')[0];
1704
+ const tagElement = this.contentElement.querySelectorAll('.webkit-html-tag-name')[0];
1692
1705
  tagElement.classList.add('violating-element');
1693
1706
  this.#updateNodeElementToIssue(tagElement, issue);
1694
1707
  }
@@ -1700,7 +1713,7 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
1700
1713
  this.#nodeElementToIssue.set(nodeElement, issues);
1701
1714
  }
1702
1715
  issues.push(issue);
1703
- this.treeOutline?.updateNodeElementToIssue(nodeElement, issues);
1716
+ this.treeElement.treeOutline?.updateNodeElementToIssue(nodeElement, issues);
1704
1717
  }
1705
1718
 
1706
1719
  removeIssue(issue: IssuesManager.Issue.Issue): void {
@@ -1726,7 +1739,7 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
1726
1739
  }
1727
1740
 
1728
1741
  #undoHighlightViolatingAttr(name: string, issue: IssuesManager.Issue.Issue): void {
1729
- const violatingAttributes = this.listItemElement.querySelectorAll('.webkit-html-attribute-name.violating-element');
1742
+ const violatingAttributes = this.contentElement.querySelectorAll('.webkit-html-attribute-name.violating-element');
1730
1743
  for (const attributeElement of violatingAttributes) {
1731
1744
  if (attributeElement.textContent === name) {
1732
1745
  this.#removeFromNodeElementToIssue(attributeElement, issue);
@@ -1738,7 +1751,7 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
1738
1751
  }
1739
1752
 
1740
1753
  #undoHighlightTagAsViolating(issue: IssuesManager.Issue.Issue): void {
1741
- const tagElement = this.listItemElement.getElementsByClassName('webkit-html-tag-name')[0];
1754
+ const tagElement = this.contentElement.querySelectorAll('.webkit-html-tag-name')[0];
1742
1755
  if (!tagElement) {
1743
1756
  return;
1744
1757
  }
@@ -1761,7 +1774,7 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
1761
1774
  } else {
1762
1775
  this.#nodeElementToIssue.set(nodeElement, issues);
1763
1776
  }
1764
- this.treeOutline?.updateNodeElementToIssue(nodeElement, issues);
1777
+ this.treeElement.treeOutline?.updateNodeElementToIssue(nodeElement, issues);
1765
1778
  }
1766
1779
 
1767
1780
  expandedChildrenLimit(): number {
@@ -1773,13 +1786,13 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
1773
1786
  }
1774
1787
 
1775
1788
  onTopLayerIndexChanged(): void {
1776
- this.performUpdate();
1789
+ this.requestUpdate();
1777
1790
  }
1778
1791
 
1779
- override onbind(): void {
1780
- this.performUpdate();
1781
- if (this.treeOutline && !this.isClosingTag()) {
1782
- this.treeOutline.treeElementByNode.set(this.nodeInternal, this);
1792
+ onbind(): void {
1793
+ this.requestUpdate();
1794
+ if (this.treeElement.treeOutline && !this.treeElement.isClosingTag()) {
1795
+ this.treeElement.treeOutline.treeElementByNode.set(this.nodeInternal, this.treeElement);
1783
1796
  this.nodeInternal.addEventListener(
1784
1797
  SDK.DOMModel.DOMNodeEvents.TOP_LAYER_INDEX_CHANGED, this.onTopLayerIndexChanged, this);
1785
1798
  this.nodeInternal.addEventListener(
@@ -1866,16 +1879,17 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
1866
1879
  editorState: null,
1867
1880
  editorWidth: null,
1868
1881
  },
1869
- {}, this.listItemElement);
1882
+ {}, this.contentElement);
1870
1883
  }
1871
1884
 
1872
- override onunbind(): void {
1885
+ onunbind(): void {
1873
1886
  if (this.editing) {
1874
1887
  this.editing.cancel();
1875
1888
  }
1876
1889
  this.clearView();
1877
- if (this.treeOutline && this.treeOutline.treeElementByNode.get(this.nodeInternal) === this) {
1878
- this.treeOutline.treeElementByNode.delete(this.nodeInternal);
1890
+ if (this.treeElement.treeOutline &&
1891
+ this.treeElement.treeOutline.treeElementByNode.get(this.nodeInternal) === this.treeElement) {
1892
+ this.treeElement.treeOutline.treeElementByNode.delete(this.nodeInternal);
1879
1893
  }
1880
1894
  this.nodeInternal.removeEventListener(
1881
1895
  SDK.DOMModel.DOMNodeEvents.TOP_LAYER_INDEX_CHANGED, this.onTopLayerIndexChanged, this);
@@ -1906,22 +1920,22 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
1906
1920
 
1907
1921
  #onPersistentContainerQueryOverlayStateChanged(event: Common.EventTarget.EventTargetEvent<{enabled: boolean}>): void {
1908
1922
  this.#containerAdornerActive = event.data.enabled;
1909
- this.performUpdate();
1923
+ this.requestUpdate();
1910
1924
  }
1911
1925
 
1912
1926
  #onPersistentFlexContainerOverlayStateChanged(event: Common.EventTarget.EventTargetEvent<{enabled: boolean}>): void {
1913
1927
  this.#flexAdornerActive = event.data.enabled;
1914
- this.performUpdate();
1928
+ this.requestUpdate();
1915
1929
  }
1916
1930
 
1917
1931
  #onPersistentGridOverlayStateChanged(event: Common.EventTarget.EventTargetEvent<{enabled: boolean}>): void {
1918
1932
  this.#gridAdornerActive = event.data.enabled;
1919
- this.performUpdate();
1933
+ this.requestUpdate();
1920
1934
  }
1921
1935
 
1922
1936
  #onPersistentScrollSnapOverlayStateChanged(event: Common.EventTarget.EventTargetEvent<{enabled: boolean}>): void {
1923
1937
  this.#scrollSnapAdornerActive = event.data.enabled;
1924
- this.performUpdate();
1938
+ this.requestUpdate();
1925
1939
  }
1926
1940
 
1927
1941
  #onScrollSnapAdornerClick(event: Event): void {
@@ -1939,75 +1953,63 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
1939
1953
  }
1940
1954
  }
1941
1955
 
1942
- override onattach(): void {
1956
+ onattach(): void {
1943
1957
  if (this.#hovered) {
1944
- this.listItemElement.classList.add('hovered');
1945
- this.performUpdate();
1958
+ this.element.classList.add('hovered');
1959
+ this.requestUpdate();
1946
1960
  }
1947
1961
 
1948
- this.updateTitle();
1949
- this.listItemElement.draggable = true;
1962
+ this.treeElement.updateTitle();
1963
+ this.element.draggable = true;
1950
1964
  }
1951
1965
 
1952
- override async onpopulate(): Promise<void> {
1953
- if (this.treeOutline) {
1954
- return await this.treeOutline.populateTreeElement(this);
1966
+ async onpopulate(): Promise<void> {
1967
+ if (this.treeElement.treeOutline) {
1968
+ return await this.treeElement.treeOutline.populateTreeElement(this.treeElement);
1955
1969
  }
1956
1970
  }
1957
1971
 
1958
- override async expandRecursively(): Promise<void> {
1959
- await this.nodeInternal.getSubtree(100, true);
1960
- await super.expandRecursively(Number.MAX_VALUE);
1961
- }
1962
-
1963
- override onexpand(): void {
1964
- if (this.isClosingTag()) {
1972
+ onexpand(): void {
1973
+ if (this.treeElement.isClosingTag()) {
1965
1974
  return;
1966
1975
  }
1967
1976
 
1968
- this.updateTitle();
1977
+ this.treeElement.updateTitle();
1969
1978
  }
1970
1979
 
1971
- override oncollapse(): void {
1972
- if (this.isClosingTag()) {
1980
+ oncollapse(): void {
1981
+ if (this.treeElement.isClosingTag()) {
1973
1982
  return;
1974
1983
  }
1975
1984
 
1976
- this.updateTitle();
1985
+ this.treeElement.updateTitle();
1977
1986
  }
1978
1987
 
1979
- override select(omitFocus?: boolean, selectedByUser?: boolean): boolean {
1980
- if (this.editing) {
1981
- return false;
1982
- }
1983
- return super.select(omitFocus, selectedByUser);
1984
- }
1985
-
1986
- override onselect(selectedByUser?: boolean): boolean {
1987
- if (!this.treeOutline) {
1988
+ onselect(selectedByUser?: boolean): boolean {
1989
+ if (!this.treeElement.treeOutline) {
1988
1990
  return false;
1989
1991
  }
1990
- this.treeOutline.suppressRevealAndSelect = true;
1991
- this.treeOutline.selectDOMNode(this.nodeInternal, selectedByUser);
1992
+ this.treeElement.treeOutline.suppressRevealAndSelect = true;
1993
+ this.treeElement.treeOutline.selectDOMNode(this.nodeInternal, selectedByUser);
1992
1994
  if (selectedByUser) {
1993
1995
  this.nodeInternal.highlight();
1994
1996
  Host.userMetrics.actionTaken(Host.UserMetrics.Action.ChangeInspectedNodeInElementsPanel);
1995
1997
  }
1996
- this.performUpdate();
1997
- this.treeOutline.suppressRevealAndSelect = false;
1998
+ this.requestUpdate();
1999
+ this.treeElement.treeOutline.suppressRevealAndSelect = false;
1998
2000
  return true;
1999
2001
  }
2000
2002
 
2001
- override ondelete(): boolean {
2002
- if (!this.treeOutline) {
2003
+ ondelete(): boolean {
2004
+ if (!this.treeElement.treeOutline) {
2003
2005
  return false;
2004
2006
  }
2005
- const startTagTreeElement = this.treeOutline.findTreeElement(this.nodeInternal);
2006
- startTagTreeElement ? (void startTagTreeElement.remove()) : (void this.remove());
2007
+ const startTagTreeElement = this.treeElement.treeOutline.findTreeElement(this.nodeInternal);
2008
+ startTagTreeElement ? (void (startTagTreeElement as ElementsTreeElement).remove()) : (void this.remove());
2007
2009
  return true;
2008
2010
  }
2009
2011
 
2010
- override onenter(): boolean {
2012
+ onenter(): boolean {
2011
2013
  // On Enter or Return start editing the first attribute
2012
2014
  // or create a new attribute on the selected element.
2013
2015
  if (this.editing) {
@@ -2020,29 +2022,17 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
2020
2022
  return true;
2021
2023
  }
2022
2024
 
2023
- override selectOnMouseDown(event: MouseEvent): void {
2024
- super.selectOnMouseDown(event);
2025
-
2026
- if (this.editing) {
2027
- return;
2028
- }
2029
-
2030
- // Prevent selecting the nearest word on double click.
2031
- if (event.detail >= 2) {
2032
- event.preventDefault();
2033
- }
2034
- }
2035
-
2036
- override ondblclick(event: Event): boolean {
2037
- if (this.editing || this.isClosingTag()) {
2025
+ ondblclick(event: Event): boolean {
2026
+ if (this.editing || this.treeElement.isClosingTag()) {
2038
2027
  return false;
2039
2028
  }
2040
- if (this.startEditingTarget((event.target as Element))) {
2029
+ const target = (event.composedPath()[0] || event.target) as Element;
2030
+ if (this.startEditingTarget(target)) {
2041
2031
  return false;
2042
2032
  }
2043
2033
 
2044
- if (this.isExpandable() && !this.expanded) {
2045
- this.expand();
2034
+ if (this.treeElement.isExpandable() && !this.treeElement.expanded) {
2035
+ this.treeElement.expand();
2046
2036
  }
2047
2037
  return false;
2048
2038
  }
@@ -2068,7 +2058,7 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
2068
2058
  }
2069
2059
 
2070
2060
  private startEditingTarget(eventTarget: Element): boolean {
2071
- if (!this.treeOutline || this.treeOutline.selectedDOMNode() !== this.nodeInternal) {
2061
+ if (!this.treeElement.treeOutline || this.treeElement.treeOutline.selectedDOMNode() !== this.nodeInternal) {
2072
2062
  return false;
2073
2063
  }
2074
2064
 
@@ -2090,19 +2080,19 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
2090
2080
 
2091
2081
  const tagName = eventTarget.enclosingNodeOrSelfWithClass('webkit-html-tag-name');
2092
2082
  if (tagName) {
2093
- return this.startEditingTagName(tagName);
2083
+ return this.treeElement.startEditingTagName(tagName);
2094
2084
  }
2095
2085
 
2096
2086
  const newAttribute = eventTarget.enclosingNodeOrSelfWithClass('add-attribute');
2097
2087
  if (newAttribute) {
2098
- return this.addNewAttribute();
2088
+ return this.treeElement.addNewAttribute();
2099
2089
  }
2100
2090
 
2101
2091
  return false;
2102
2092
  }
2103
2093
 
2104
2094
  private showContextMenu(event: Event): void {
2105
- this.treeOutline && void this.treeOutline.showContextMenu(this, event);
2095
+ this.treeElement.treeOutline && void this.treeElement.treeOutline.showContextMenu(this.treeElement, event);
2106
2096
  }
2107
2097
 
2108
2098
  private revealHTMLInSources(): void {
@@ -2118,8 +2108,9 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
2118
2108
 
2119
2109
  async populateTagContextMenu(contextMenu: UI.ContextMenu.ContextMenu, event: Event): Promise<void> {
2120
2110
  // Add attribute-related actions.
2121
- const treeElement =
2122
- this.isClosingTag() && this.treeOutline ? this.treeOutline.findTreeElement(this.nodeInternal) : this;
2111
+ const treeElement = this.treeElement.isClosingTag() && this.treeElement.treeOutline ?
2112
+ this.treeElement.treeOutline.findTreeElement(this.nodeInternal) as ElementsTreeElement | null :
2113
+ this.treeElement;
2123
2114
  if (!treeElement) {
2124
2115
  return;
2125
2116
  }
@@ -2127,7 +2118,7 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
2127
2118
  i18nString(UIStrings.addAttribute), treeElement.addNewAttribute.bind(treeElement),
2128
2119
  {jslogContext: 'add-attribute'});
2129
2120
 
2130
- const target = (event.target as Element);
2121
+ const target = (event.composedPath()[0] || event.target) as Element;
2131
2122
  const attribute = target.enclosingNodeOrSelfWithClass('webkit-html-attribute');
2132
2123
  const newAttribute = target.enclosingNodeOrSelfWithClass('add-attribute');
2133
2124
  if (attribute && !newAttribute) {
@@ -2136,7 +2127,7 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
2136
2127
  {jslogContext: 'edit-attribute'});
2137
2128
  }
2138
2129
  await this.populateNodeContextMenu(contextMenu);
2139
- ElementsTreeElement.populateForcedPseudoStateItems(contextMenu, treeElement.node());
2130
+ ElementsTreeWidget.populateForcedPseudoStateItems(contextMenu, treeElement.node());
2140
2131
  this.populateScrollIntoView(contextMenu);
2141
2132
  contextMenu.viewSection().appendItem(i18nString(UIStrings.focus), async () => {
2142
2133
  await this.nodeInternal.focus();
@@ -2144,7 +2135,7 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
2144
2135
  }
2145
2136
 
2146
2137
  populatePseudoElementContextMenu(contextMenu: UI.ContextMenu.ContextMenu): void {
2147
- if (this.childCount() !== 0) {
2138
+ if (this.treeElement.childCount() !== 0) {
2148
2139
  this.populateExpandRecursively(contextMenu);
2149
2140
  }
2150
2141
 
@@ -2152,9 +2143,9 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
2152
2143
  }
2153
2144
 
2154
2145
  private populateExpandRecursively(contextMenu: UI.ContextMenu.ContextMenu): void {
2155
- contextMenu.viewSection().appendItem(
2156
- i18nString(UIStrings.expandRecursively), this.expandRecursively.bind(this),
2157
- {jslogContext: 'expand-recursively'});
2146
+ contextMenu.viewSection().appendItem(i18nString(UIStrings.expandRecursively),
2147
+ this.treeElement.expandRecursively.bind(this.treeElement),
2148
+ {jslogContext: 'expand-recursively'});
2158
2149
  }
2159
2150
 
2160
2151
  private populateScrollIntoView(contextMenu: UI.ContextMenu.ContextMenu): void {
@@ -2167,10 +2158,11 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
2167
2158
  return UI.ActionRegistry.ActionRegistry.instance().hasAction('freestyler.elements-floating-button');
2168
2159
  }
2169
2160
 
2170
- async populateTextContextMenu(contextMenu: UI.ContextMenu.ContextMenu, textNode: Element): Promise<void> {
2161
+ async populateTextContextMenu(contextMenu: UI.ContextMenu.ContextMenu, textNode: Node): Promise<void> {
2171
2162
  if (!this.editing) {
2172
- contextMenu.editSection().appendItem(
2173
- i18nString(UIStrings.editText), this.startEditingTextNode.bind(this, textNode), {jslogContext: 'edit-text'});
2163
+ contextMenu.editSection().appendItem(i18nString(UIStrings.editText),
2164
+ this.startEditingTextNode.bind(this, textNode as Element),
2165
+ {jslogContext: 'edit-text'});
2174
2166
  }
2175
2167
  return await this.populateNodeContextMenu(contextMenu);
2176
2168
  }
@@ -2187,7 +2179,7 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
2187
2179
 
2188
2180
  const createShortcut = UI.KeyboardShortcut.KeyboardShortcut.shortcutToString.bind(null);
2189
2181
  const modifier = UI.KeyboardShortcut.Modifiers.CtrlOrMeta.value;
2190
- const treeOutline = this.treeOutline;
2182
+ const treeOutline = this.treeElement.treeOutline;
2191
2183
  if (!treeOutline) {
2192
2184
  return;
2193
2185
  }
@@ -2390,8 +2382,10 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
2390
2382
  menuItem.setShortcut(createShortcut('V', modifier));
2391
2383
 
2392
2384
  menuItem = contextMenu.debugSection().appendCheckboxItem(
2393
- i18nString(UIStrings.hideElement), treeOutline.toggleHideElement.bind(treeOutline, this.nodeInternal),
2394
- {checked: treeOutline.isToggledToHidden(this.nodeInternal), jslogContext: 'elements.hide-element'});
2385
+ i18nString(UIStrings.hideElement), treeOutline.toggleHideElement.bind(treeOutline, this.nodeInternal), {
2386
+ checked: treeOutline.isToggledToHidden(this.nodeInternal),
2387
+ jslogContext: 'elements.hide-element',
2388
+ });
2395
2389
  menuItem.setShortcut(
2396
2390
  UI.ShortcutRegistry.ShortcutRegistry.instance().shortcutTitleForAction('elements.hide-element') || '');
2397
2391
 
@@ -2401,7 +2395,8 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
2401
2395
  }
2402
2396
 
2403
2397
  this.populateExpandRecursively(contextMenu);
2404
- contextMenu.viewSection().appendItem(i18nString(UIStrings.collapseChildren), this.collapseChildren.bind(this),
2398
+ contextMenu.viewSection().appendItem(i18nString(UIStrings.collapseChildren),
2399
+ this.treeElement.collapseChildren.bind(this.treeElement),
2405
2400
  {jslogContext: 'collapse-children'});
2406
2401
  contextMenu.viewSection().appendItem(i18nString(UIStrings.switchToAccessibilityTree),
2407
2402
  () => ElementsPanel.instance().toggleAccessibilityTree(),
@@ -2424,7 +2419,7 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
2424
2419
  }
2425
2420
 
2426
2421
  async populateProcessingElementContextMenu(contextMenu: UI.ContextMenu.ContextMenu): Promise<void> {
2427
- const treeOutline = this.treeOutline;
2422
+ const treeOutline = this.treeElement.treeOutline;
2428
2423
  if (!treeOutline) {
2429
2424
  return;
2430
2425
  }
@@ -2442,20 +2437,20 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
2442
2437
  }
2443
2438
 
2444
2439
  private startEditing(): boolean|undefined {
2445
- if (!this.treeOutline || this.treeOutline.selectedDOMNode() !== this.nodeInternal) {
2440
+ if (!this.treeElement.treeOutline || this.treeElement.treeOutline.selectedDOMNode() !== this.nodeInternal) {
2446
2441
  return;
2447
2442
  }
2448
2443
 
2449
- const listItem = this.listItemElement;
2444
+ const listItem = this.element;
2450
2445
 
2451
- if (isOpeningTag(this.tagTypeContext) && this.tagTypeContext.canAddAttributes) {
2446
+ if (isOpeningTag(this.treeElement.tagTypeContext) && this.treeElement.tagTypeContext.canAddAttributes) {
2452
2447
  const attribute = listItem.getElementsByClassName('webkit-html-attribute')[0];
2453
2448
  if (attribute) {
2454
2449
  return this.startEditingAttribute(
2455
2450
  attribute, attribute.getElementsByClassName('webkit-html-attribute-value')[0]);
2456
2451
  }
2457
2452
 
2458
- return this.addNewAttribute();
2453
+ return this.treeElement.addNewAttribute();
2459
2454
  }
2460
2455
 
2461
2456
  if (this.nodeInternal.nodeType() === Node.TEXT_NODE) {
@@ -2466,22 +2461,22 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
2466
2461
  }
2467
2462
 
2468
2463
  if (this.nodeInternal.nodeType() === Node.PROCESSING_INSTRUCTION_NODE) {
2469
- return this.startEditingProcessingInstructionValue();
2464
+ return this.treeElement.startEditingProcessingInstructionValue();
2470
2465
  }
2471
2466
 
2472
2467
  return;
2473
2468
  }
2474
2469
 
2475
- private startEditingProcessingInstructionValue(): boolean|undefined {
2470
+ startEditingProcessingInstructionValue(): boolean|undefined {
2476
2471
  const processingInstructionValue =
2477
- this.listItemElement.getElementsByClassName('webkit-html-processing-instruction-value')[0];
2472
+ this.contentElement.querySelectorAll('.webkit-html-processing-instruction-value')[0];
2478
2473
  if (processingInstructionValue) {
2479
2474
  return this.startEditingTextNode(processingInstructionValue);
2480
2475
  }
2481
2476
  return;
2482
2477
  }
2483
2478
 
2484
- private addNewAttribute(): boolean {
2479
+ addNewAttribute(): boolean {
2485
2480
  // Cannot just convert the textual html into an element without
2486
2481
  // a parent node. Use a temporary span container for the HTML.
2487
2482
  const container = document.createDocumentFragment();
@@ -2492,14 +2487,14 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
2492
2487
  attr.style.marginRight = '2px'; // overrides the .editing margin rule
2493
2488
  attr.setAttribute('jslog', `${VisualLogging.value('new-attribute').track({change: true, resize: true})}`);
2494
2489
 
2495
- const tag = this.listItemElement.getElementsByClassName('webkit-html-tag')[0];
2490
+ const tag = this.contentElement.querySelectorAll('.webkit-html-tag')[0];
2496
2491
  this.insertInLastAttributePosition(tag, attr);
2497
2492
  attr.scrollIntoViewIfNeeded(true);
2498
2493
  return this.startEditingAttribute(attr, attr);
2499
2494
  }
2500
2495
 
2501
2496
  triggerEditAttribute(attributeName: string): boolean|undefined {
2502
- const attributeElements = this.listItemElement.getElementsByClassName('webkit-html-attribute-name');
2497
+ const attributeElements = this.contentElement.querySelectorAll('.webkit-html-attribute-name');
2503
2498
  for (let i = 0, len = attributeElements.length; i < len; ++i) {
2504
2499
  if (attributeElements[i].textContent === attributeName) {
2505
2500
  for (let elem: (ChildNode|null) = attributeElements[i].nextSibling; elem; elem = elem.nextSibling) {
@@ -2517,7 +2512,7 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
2517
2512
  }
2518
2513
 
2519
2514
  private startEditingAttribute(attribute: Element, elementForSelection: Element): boolean {
2520
- console.assert(this.listItemElement.isAncestor(attribute));
2515
+ console.assert(this.element.isAncestor(attribute));
2521
2516
 
2522
2517
  if (UI.UIUtils.isBeingEdited(attribute)) {
2523
2518
  return true;
@@ -2586,7 +2581,7 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
2586
2581
 
2587
2582
  this.updateEditorHandles(attribute, config);
2588
2583
 
2589
- const componentSelection = this.listItemElement.getComponentSelection();
2584
+ const componentSelection = this.element.getComponentSelection();
2590
2585
  componentSelection?.selectAllChildren(elementForSelection);
2591
2586
 
2592
2587
  return true;
@@ -2611,15 +2606,15 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
2611
2606
  const config = new UI.InplaceEditor.Config(
2612
2607
  this.textNodeEditingCommitted.bind(this, textNode), this.editingCancelled.bind(this), null);
2613
2608
  this.updateEditorHandles(textNodeElement, config);
2614
- const componentSelection = this.listItemElement.getComponentSelection();
2609
+ const componentSelection = this.element.getComponentSelection();
2615
2610
  componentSelection?.selectAllChildren(textNodeElement);
2616
2611
 
2617
2612
  return true;
2618
2613
  }
2619
2614
 
2620
- private startEditingTagName(tagNameElement?: Element): boolean {
2615
+ startEditingTagName(tagNameElement?: Element): boolean {
2621
2616
  if (!tagNameElement) {
2622
- tagNameElement = this.listItemElement.getElementsByClassName('webkit-html-tag-name')[0];
2617
+ tagNameElement = this.contentElement.querySelectorAll('.webkit-html-tag-name')[0];
2623
2618
  if (!tagNameElement) {
2624
2619
  return false;
2625
2620
  }
@@ -2651,7 +2646,7 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
2651
2646
  };
2652
2647
 
2653
2648
  function editingCommitted(
2654
- this: ElementsTreeElement,
2649
+ this: ElementsTreeWidget,
2655
2650
  element: Element,
2656
2651
  newTagName: string,
2657
2652
  oldText: string|null,
@@ -2666,7 +2661,7 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
2666
2661
  this.tagNameEditingCommitted(element, newTagName, oldText, tagName, moveDirection);
2667
2662
  }
2668
2663
 
2669
- function editingCancelled(this: ElementsTreeElement, element: Element, tagName: string|null): void {
2664
+ function editingCancelled(this: ElementsTreeWidget, element: Element, tagName: string|null): void {
2670
2665
  if (!tagNameElement) {
2671
2666
  return;
2672
2667
  }
@@ -2681,7 +2676,7 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
2681
2676
  const config =
2682
2677
  new UI.InplaceEditor.Config<string|null>(editingCommitted.bind(this), editingCancelled.bind(this), tagName);
2683
2678
  this.updateEditorHandles(tagNameElement, config);
2684
- const componentSelection = this.listItemElement.getComponentSelection();
2679
+ const componentSelection = this.element.getComponentSelection();
2685
2680
  componentSelection?.selectAllChildren(tagNameElement);
2686
2681
  return true;
2687
2682
  }
@@ -2710,8 +2705,8 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
2710
2705
  }
2711
2706
 
2712
2707
  // Hide children item.
2713
- if (this.childrenListElement) {
2714
- this.childrenListElement.style.display = 'none';
2708
+ if (this.treeElement.childrenListElement) {
2709
+ this.treeElement.childrenListElement.style.display = 'none';
2715
2710
  }
2716
2711
  const initialValue = convertUnicodeCharsToHTMLEntities(maybeInitialValue).text;
2717
2712
  // Append editor.
@@ -2793,41 +2788,43 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
2793
2788
  }),
2794
2789
  ],
2795
2790
  });
2796
- this.performUpdate();
2791
+ this.requestUpdate();
2797
2792
  resize.call(this);
2798
- this.#editorRef?.focus();
2799
2793
  this.editing = {commit: commit.bind(this), cancel: dispose.bind(this), resize: resize.bind(this)};
2800
- this.treeOutline?.setMultilineEditing(this.editing);
2794
+ (this.treeElement.treeOutline as ElementsTreeOutline | null)?.setMultilineEditing(this.editing);
2795
+ await this.updateComplete;
2796
+ this.#editorRef?.focus();
2801
2797
 
2802
- function resize(this: ElementsTreeElement): void {
2803
- if (this.treeOutline) {
2804
- this.#editorWidth = this.treeOutline.visibleWidth() - this.computeLeftIndent() - 30;
2805
- this.performUpdate();
2798
+ function resize(this: ElementsTreeWidget): void {
2799
+ if (this.treeElement.treeOutline) {
2800
+ this.#editorWidth =
2801
+ (this.treeElement.treeOutline as ElementsTreeOutline).visibleWidth() - this.computeLeftIndent() - 30;
2802
+ this.requestUpdate();
2806
2803
  }
2807
2804
  }
2808
2805
 
2809
- function commit(this: ElementsTreeElement): void {
2806
+ function commit(this: ElementsTreeWidget): void {
2810
2807
  if (this.#editorRef) {
2811
2808
  commitCallback(initialValue, this.#editorRef.editor.state.doc.toString());
2812
2809
  }
2813
2810
  dispose.call(this);
2814
2811
  }
2815
2812
 
2816
- function dispose(this: ElementsTreeElement): void {
2813
+ function dispose(this: ElementsTreeWidget): void {
2817
2814
  if (!this.#editorRef) {
2818
2815
  return;
2819
2816
  }
2820
2817
  this.editing = null;
2821
2818
  this.#editorState = null;
2822
- this.performUpdate();
2819
+ this.requestUpdate();
2823
2820
  // Unhide children item.
2824
- if (this.childrenListElement) {
2825
- this.childrenListElement.style.removeProperty('display');
2821
+ if (this.treeElement.childrenListElement) {
2822
+ this.treeElement.childrenListElement.style.removeProperty('display');
2826
2823
  }
2827
2824
 
2828
- if (this.treeOutline) {
2829
- this.treeOutline.setMultilineEditing(null);
2830
- this.treeOutline.focus();
2825
+ if (this.treeElement.treeOutline) {
2826
+ (this.treeElement.treeOutline as ElementsTreeOutline).setMultilineEditing(null);
2827
+ this.treeElement.treeOutline.focus();
2831
2828
  }
2832
2829
 
2833
2830
  disposeCallback();
@@ -2843,9 +2840,9 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
2843
2840
  ): void {
2844
2841
  this.editing = null;
2845
2842
 
2846
- const treeOutline = this.treeOutline;
2843
+ const treeOutline = this.treeElement.treeOutline;
2847
2844
 
2848
- function moveToNextAttributeIfNeeded(this: ElementsTreeElement, error?: string|null): void {
2845
+ function moveToNextAttributeIfNeeded(this: ElementsTreeWidget, error?: string|null): void {
2849
2846
  if (error) {
2850
2847
  this.editingCancelled(element, attributeName);
2851
2848
  }
@@ -2855,7 +2852,7 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
2855
2852
  }
2856
2853
 
2857
2854
  if (treeOutline) {
2858
- treeOutline.runPendingUpdates();
2855
+ (treeOutline as ElementsTreeOutline).runPendingUpdates();
2859
2856
  treeOutline.focus();
2860
2857
  }
2861
2858
 
@@ -2868,12 +2865,12 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
2868
2865
 
2869
2866
  if (moveDirection === 'backward') {
2870
2867
  if (i === 0) {
2871
- this.startEditingTagName();
2868
+ this.treeElement.startEditingTagName();
2872
2869
  } else {
2873
2870
  this.triggerEditAttribute(attributes[i - 1].name);
2874
2871
  }
2875
2872
  } else if (i === attributes.length - 1) {
2876
- this.addNewAttribute();
2873
+ this.treeElement.addNewAttribute();
2877
2874
  } else {
2878
2875
  this.triggerEditAttribute(attributes[i + 1].name);
2879
2876
  }
@@ -2893,9 +2890,9 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
2893
2890
  }
2894
2891
  } else if (moveDirection === 'forward') {
2895
2892
  if (!Platform.StringUtilities.isWhitespace(newText)) {
2896
- this.addNewAttribute();
2893
+ this.treeElement.addNewAttribute();
2897
2894
  } else {
2898
- this.startEditingTagName();
2895
+ this.treeElement.startEditingTagName();
2899
2896
  }
2900
2897
  }
2901
2898
  }
@@ -2906,7 +2903,7 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
2906
2903
  return;
2907
2904
  }
2908
2905
 
2909
- this.updateTitle();
2906
+ this.treeElement.updateTitle();
2910
2907
  moveToNextAttributeIfNeeded.call(this);
2911
2908
  }
2912
2909
 
@@ -2930,14 +2927,14 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
2930
2927
  moveToNextAttributeIfNeeded.call(self);
2931
2928
  }
2932
2929
 
2933
- function moveToNextAttributeIfNeeded(this: ElementsTreeElement): void {
2930
+ function moveToNextAttributeIfNeeded(this: ElementsTreeWidget): void {
2934
2931
  if (this.nodeInternal.nodeType() === Node.PROCESSING_INSTRUCTION_NODE) {
2935
- this.startEditingProcessingInstructionValue();
2932
+ this.treeElement.startEditingProcessingInstructionValue();
2936
2933
  return;
2937
2934
  }
2938
2935
 
2939
2936
  if (moveDirection !== 'forward') {
2940
- this.addNewAttribute();
2937
+ this.treeElement.addNewAttribute();
2941
2938
  return;
2942
2939
  }
2943
2940
 
@@ -2945,7 +2942,7 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
2945
2942
  if (attributes.length > 0) {
2946
2943
  this.triggerEditAttribute(attributes[0].name);
2947
2944
  } else {
2948
- this.addNewAttribute();
2945
+ this.treeElement.addNewAttribute();
2949
2946
  }
2950
2947
  }
2951
2948
 
@@ -2955,8 +2952,8 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
2955
2952
  return;
2956
2953
  }
2957
2954
 
2958
- const treeOutline = this.treeOutline;
2959
- const wasExpanded = this.expanded;
2955
+ const treeOutline = this.treeElement.treeOutline;
2956
+ const wasExpanded = this.treeElement.expanded;
2960
2957
 
2961
2958
  this.nodeInternal.setNodeName(newText, (error, newNode) => {
2962
2959
  if (error || !newNode) {
@@ -2968,18 +2965,21 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
2968
2965
  }
2969
2966
 
2970
2967
  Badges.UserBadges.instance().recordAction(Badges.BadgeAction.DOM_ELEMENT_OR_ATTRIBUTE_EDITED);
2971
- const newTreeItem = treeOutline.selectNodeAfterEdit(wasExpanded, error, newNode);
2972
- // TODO(crbug.com/1172300) Ignored during the jsdoc to ts migration
2973
- // @ts-expect-error
2974
- moveToNextAttributeIfNeeded.call(newTreeItem);
2968
+ const newTreeItem =
2969
+ (treeOutline as ElementsTreeOutline).selectNodeAfterEdit(wasExpanded, error, newNode) as ElementsTreeElement |
2970
+ null;
2971
+ if (newTreeItem) {
2972
+ moveToNextAttributeIfNeeded.call(newTreeItem.widget);
2973
+ }
2975
2974
  });
2976
2975
  }
2977
2976
 
2978
2977
  private textNodeEditingCommitted(textNode: SDK.DOMModel.DOMNode, _element: Element, newText: string): void {
2979
2978
  this.editing = null;
2980
2979
 
2981
- function callback(this: ElementsTreeElement): void {
2982
- this.updateTitle();
2980
+ function callback(this: ElementsTreeWidget): void {
2981
+ this.#clearDOMNextUpdate = true;
2982
+ this.treeElement.updateTitle();
2983
2983
  }
2984
2984
  textNode.setNodeValue(newText, callback.bind(this));
2985
2985
  }
@@ -2988,7 +2988,8 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
2988
2988
  this.editing = null;
2989
2989
 
2990
2990
  // Need to restore attributes structure.
2991
- this.updateTitle();
2991
+ this.#clearDOMNextUpdate = true;
2992
+ this.treeElement.updateTitle();
2992
2993
  }
2993
2994
 
2994
2995
  private distinctClosingTagElement(): Element|null {
@@ -2996,15 +2997,15 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
2996
2997
 
2997
2998
  // For an expanded element, it will be the last element with class "close"
2998
2999
  // in the child element list.
2999
- if (this.expanded) {
3000
- const closers = this.childrenListElement.querySelectorAll('.close');
3000
+ if (this.treeElement.expanded) {
3001
+ const closers = this.treeElement.childrenListElement.querySelectorAll('.close');
3001
3002
  return closers[closers.length - 1];
3002
3003
  }
3003
3004
 
3004
3005
  // Remaining cases are single line non-expanded elements with a closing
3005
3006
  // tag, or HTML elements without a closing tag (such as <br>). Return
3006
3007
  // null in the case where there isn't a closing tag.
3007
- const tags = this.listItemElement.getElementsByClassName('webkit-html-tag');
3008
+ const tags = this.contentElement.querySelectorAll('.webkit-html-tag');
3008
3009
  return tags.length === 1 ? null : tags[tags.length - 1];
3009
3010
  }
3010
3011
 
@@ -3014,11 +3015,10 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
3014
3015
  if (this.editing) {
3015
3016
  return;
3016
3017
  }
3017
- this.performUpdate(/* clearNode= */ true);
3018
3018
  this.#updateRecord = updateRecord ?? null;
3019
3019
  if (this.nodeInternal.nodeType() === Node.DOCUMENT_FRAGMENT_NODE && this.nodeInternal.isInShadowTree() &&
3020
3020
  this.nodeInternal.shadowRootType()) {
3021
- this.childrenListElement.classList.add('shadow-root');
3021
+ this.treeElement.childrenListElement.classList.add('shadow-root');
3022
3022
  let depth = 4;
3023
3023
  for (let node: (SDK.DOMModel.DOMNode|null) = (this.nodeInternal as SDK.DOMModel.DOMNode | null); depth && node;
3024
3024
  node = node.parentNode) {
@@ -3027,16 +3027,12 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
3027
3027
  }
3028
3028
  }
3029
3029
  if (!depth) {
3030
- this.childrenListElement.classList.add('shadow-root-deep');
3030
+ this.treeElement.childrenListElement.classList.add('shadow-root-deep');
3031
3031
  } else {
3032
- this.childrenListElement.classList.add('shadow-root-depth-' + depth);
3032
+ this.treeElement.childrenListElement.classList.add('shadow-root-depth-' + depth);
3033
3033
  }
3034
3034
  }
3035
3035
  this.performUpdate();
3036
- if (this.#contentElement) {
3037
- // fixme: we probably do not need a title element in the new tree outline.
3038
- this.title = this.#contentElement;
3039
- }
3040
3036
  this.updateDecorations();
3041
3037
 
3042
3038
  // If there is an issue with this node, make sure to update it.
@@ -3048,7 +3044,7 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
3048
3044
  }
3049
3045
 
3050
3046
  private computeLeftIndent(): number {
3051
- let treeElement: (UI.TreeOutline.TreeElement|null) = this.parent;
3047
+ let treeElement: (UI.TreeOutline.TreeElement|null) = this.treeElement.parent;
3052
3048
  let depth = 0;
3053
3049
  while (treeElement !== null) {
3054
3050
  depth++;
@@ -3056,15 +3052,15 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
3056
3052
  }
3057
3053
 
3058
3054
  /** Keep it in sync with elementsTreeOutline.css **/
3059
- return 12 * (depth - 2) + (this.isExpandable() && this.isCollapsible() ? 1 : 12);
3055
+ return 12 * (depth - 2) + (this.treeElement.isExpandable() && this.treeElement.isCollapsible() ? 1 : 12);
3060
3056
  }
3061
3057
 
3062
3058
  updateDecorations(): void {
3063
3059
  // Important to keep the entire tree node row as a clickable area for that
3064
3060
  // node.
3065
- this.listItemElement.style.setProperty('--indent', this.computeLeftIndent() + 'px');
3061
+ this.element.style.setProperty('--indent', this.computeLeftIndent() + 'px');
3066
3062
 
3067
- if (this.isClosingTag()) {
3063
+ if (this.treeElement.isClosingTag()) {
3068
3064
  return;
3069
3065
  }
3070
3066
 
@@ -3076,18 +3072,19 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
3076
3072
  }
3077
3073
 
3078
3074
  #updateDecorations(): Promise<void> {
3079
- if (!this.treeOutline) {
3075
+ if (!this.treeElement.treeOutline) {
3080
3076
  return Promise.resolve();
3081
3077
  }
3082
3078
 
3083
3079
  const node = this.nodeInternal;
3084
3080
 
3085
- if (!this.treeOutline.decoratorExtensions) {
3086
- this.treeOutline.decoratorExtensions = getRegisteredDecorators();
3081
+ const outline = this.treeElement.treeOutline as ElementsTreeOutline;
3082
+ if (!outline.decoratorExtensions) {
3083
+ outline.decoratorExtensions = getRegisteredDecorators();
3087
3084
  }
3088
3085
 
3089
3086
  const markerToExtension = new Map<string, MarkerDecoratorRegistration>();
3090
- for (const decoratorExtension of this.treeOutline.decoratorExtensions) {
3087
+ for (const decoratorExtension of (outline.decoratorExtensions || [])) {
3091
3088
  markerToExtension.set(decoratorExtension.marker, decoratorExtension);
3092
3089
  }
3093
3090
 
@@ -3120,13 +3117,13 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
3120
3117
 
3121
3118
  return Promise.all(promises).then(updateDecorationsUI.bind(this));
3122
3119
 
3123
- function updateDecorationsUI(this: ElementsTreeElement): void {
3120
+ function updateDecorationsUI(this: ElementsTreeWidget): void {
3124
3121
  this.#decorations = decorations;
3125
3122
  this.#descendantDecorations = descendantDecorations;
3126
3123
 
3127
3124
  if (!decorations.length && !descendantDecorations.length) {
3128
3125
  this.#decorationsTooltip = '';
3129
- this.performUpdate();
3126
+ this.requestUpdate();
3130
3127
  return;
3131
3128
  }
3132
3129
 
@@ -3134,26 +3131,26 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
3134
3131
  for (const decoration of decorations) {
3135
3132
  tooltip.push(decoration.title);
3136
3133
  }
3137
- if (!this.expanded && descendantDecorations.length) {
3134
+ if (!this.treeElement.expanded && descendantDecorations.length) {
3138
3135
  tooltip.push(i18nString(UIStrings.children));
3139
3136
  for (const decoration of descendantDecorations) {
3140
3137
  tooltip.push(decoration.title);
3141
3138
  }
3142
3139
  }
3143
3140
  this.#decorationsTooltip = tooltip.join('\n');
3144
- this.performUpdate();
3141
+ this.requestUpdate();
3145
3142
  }
3146
3143
  }
3147
3144
 
3148
3145
  async remove(): Promise<void> {
3149
- if (this.treeOutline?.isToggledToHidden(this.nodeInternal)) {
3146
+ if (this.treeElement.treeOutline?.isToggledToHidden(this.nodeInternal)) {
3150
3147
  // Unhide the node before removing. This avoids inconsistent state if the node is restored via undo.
3151
- await this.treeOutline.toggleHideElement(this.nodeInternal);
3148
+ await (this.treeElement.treeOutline as ElementsTreeOutline).toggleHideElement(this.nodeInternal);
3152
3149
  }
3153
3150
  if (this.nodeInternal.pseudoType()) {
3154
3151
  return;
3155
3152
  }
3156
- const parentElement = this.parent;
3153
+ const parentElement = this.treeElement.parent;
3157
3154
  if (!parentElement) {
3158
3155
  return;
3159
3156
  }
@@ -3250,7 +3247,7 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
3250
3247
  return;
3251
3248
  }
3252
3249
 
3253
- const text = this.listItemElement.textContent || '';
3250
+ const text = this.contentElement.deepTextContent();
3254
3251
  const regexObject = Platform.StringUtilities.createPlainTextSearchRegex(this.searchQuery, 'gi');
3255
3252
 
3256
3253
  const matchRanges = [];
@@ -3266,7 +3263,7 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
3266
3263
  }
3267
3264
 
3268
3265
  this.#highlights = Highlighting.HighlightManager.HighlightManager.instance().highlightOrderedTextRanges(
3269
- this.listItemElement, matchRanges);
3266
+ this.contentElement, matchRanges);
3270
3267
  }
3271
3268
 
3272
3269
  private editAsHTML(): void {
@@ -3283,7 +3280,7 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
3283
3280
  }
3284
3281
 
3285
3282
  async #updateAdorners(): Promise<void> {
3286
- if (this.isClosingTag()) {
3283
+ if (this.treeElement.isClosingTag()) {
3287
3284
  return;
3288
3285
  }
3289
3286
  const node = this.node();
@@ -3294,7 +3291,7 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
3294
3291
  } else {
3295
3292
  this.#layout = null;
3296
3293
  }
3297
- this.performUpdate();
3294
+ this.requestUpdate();
3298
3295
  }
3299
3296
 
3300
3297
  async #onPopoverAdornerClick(event: Event): Promise<void> {
@@ -3309,7 +3306,7 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
3309
3306
  if (this.#popoverAdornerActive) {
3310
3307
  Badges.UserBadges.instance().recordAction(Badges.BadgeAction.MODERN_DOM_BADGE_CLICKED);
3311
3308
  }
3312
- this.performUpdate();
3309
+ this.requestUpdate();
3313
3310
  }
3314
3311
 
3315
3312
  async #onInterestAdornerClick(event: Event): Promise<void> {
@@ -3324,7 +3321,7 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
3324
3321
  if (this.#interestAdornerActive) {
3325
3322
  Badges.UserBadges.instance().recordAction(Badges.BadgeAction.MODERN_DOM_BADGE_CLICKED);
3326
3323
  }
3327
- this.performUpdate();
3324
+ this.requestUpdate();
3328
3325
  }
3329
3326
 
3330
3327
  #onStartingStyleAdornerClick(event: Event): void {
@@ -3341,46 +3338,272 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
3341
3338
  model.forceStartingStyle(node, true);
3342
3339
  }
3343
3340
  this.#startingStyleAdornerActive = !this.#startingStyleAdornerActive;
3344
- this.performUpdate();
3341
+ this.requestUpdate();
3345
3342
  }
3343
+ }
3344
+ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
3345
+ widget: ElementsTreeWidget;
3346
+ widgetWrapper: HTMLElement;
3347
+ nodeInternal: SDK.DOMModel.DOMNode;
3348
+ tagTypeContext: TagTypeContext;
3346
3349
 
3347
- async #onCustomElementAdornerClick(event: Event): Promise<void> {
3348
- event.stopPropagation();
3349
- const node = this.node();
3350
- const object = await node.resolveToObject('');
3351
- if (!object) {
3352
- return;
3350
+ constructor(node: SDK.DOMModel.DOMNode, isClosingTag?: boolean) {
3351
+ super();
3352
+ this.nodeInternal = node;
3353
+ this.listItemElement.setAttribute(
3354
+ 'jslog', `${VisualLogging.treeItem().parent('elementsTreeOutline').track({
3355
+ keydown: 'ArrowUp|ArrowDown|ArrowLeft|ArrowRight|Backspace|Delete|Enter|Space|Home|End',
3356
+ resize: true,
3357
+ drag: true,
3358
+ click: true,
3359
+ })}`);
3360
+
3361
+ if (isClosingTag) {
3362
+ this.tagTypeContext = {tagType: TagType.CLOSING};
3363
+ } else {
3364
+ this.tagTypeContext = {
3365
+ tagType: TagType.OPENING,
3366
+ canAddAttributes: this.nodeInternal.nodeType() === Node.ELEMENT_NODE,
3367
+ };
3353
3368
  }
3354
- let constructorObject: SDK.RemoteObject.RemoteObject|null = null;
3355
- try {
3356
- const result = await object.callFunction(function(this: Element): unknown {
3357
- const selector = this.getAttribute('is') || this.tagName.toLowerCase();
3358
- return (typeof customElements !== 'undefined' && customElements.get(selector)) || this.constructor;
3359
- });
3360
- constructorObject = result.object;
3361
- } finally {
3362
- object.release();
3369
+ this.widgetWrapper = document.createElement('div');
3370
+ this.widgetWrapper.style.display = 'contents';
3371
+ this.title = this.widgetWrapper;
3372
+ this.widget = new ElementsTreeWidget(this);
3373
+ this.widget.element.classList.remove('vbox', 'flex-auto');
3374
+ this.widget.element.style.display = 'contents';
3375
+ this.widget.markAsRoot();
3376
+ this.widget.show(this.widgetWrapper);
3377
+
3378
+ if (this.nodeInternal.retained && !this.isClosingTag()) {
3379
+ this.setLeadingIcons([
3380
+ html`<devtools-icon class="extra-small" name="small-status-dot" style="color:var(--icon-error); vertical-align:middle"></devtools-icon>`,
3381
+ ]);
3382
+ this.listItemNode.classList.add('detached-elements-detached-node');
3383
+ this.listItemNode.style.setProperty('display', '-webkit-box');
3384
+ this.listItemNode.setAttribute('title', 'Retained Node');
3363
3385
  }
3364
- if (!constructorObject) {
3386
+
3387
+ if (this.nodeInternal.detached && !this.isClosingTag()) {
3388
+ this.listItemNode.setAttribute('title', 'Detached Tree Node');
3389
+ }
3390
+ }
3391
+
3392
+ highlightSearchResults(searchQuery: string): void {
3393
+ this.widget.highlightSearchResults(searchQuery);
3394
+ }
3395
+ hideSearchHighlights(): void {
3396
+ this.widget.hideSearchHighlights();
3397
+ }
3398
+ copyStyles(): Promise<void> {
3399
+ return this.widget.copyStyles();
3400
+ }
3401
+ addIssue(issue: IssuesManager.Issue.Issue): void {
3402
+ this.widget.addIssue(issue);
3403
+ }
3404
+ get issuesByNodeElement(): Map<Element, IssuesManager.Issue.Issue[]> {
3405
+ return this.widget.issuesByNodeElement;
3406
+ }
3407
+ removeIssue(issue: IssuesManager.Issue.Issue): void {
3408
+ this.widget.removeIssue(issue);
3409
+ }
3410
+ setInClipboard(inClipboard: boolean): void {
3411
+ this.widget.setInClipboard(inClipboard);
3412
+ if (this.listItemElement) {
3413
+ this.listItemElement.classList.toggle('in-clipboard', inClipboard);
3414
+ }
3415
+ }
3416
+ get isEditing(): boolean {
3417
+ return this.widget.isEditing;
3418
+ }
3419
+ expandedChildrenLimit(): number {
3420
+ return this.widget.expandedChildrenLimit();
3421
+ }
3422
+
3423
+ setExpandedChildrenLimit(limit: number): void {
3424
+ this.widget.setExpandedChildrenLimit(limit);
3425
+ }
3426
+ highlightAttribute(name: string): void {
3427
+ this.widget.highlightAttribute(name);
3428
+ }
3429
+ populateTextContextMenu(contextMenu: UI.ContextMenu.ContextMenu, textNode: Node): Promise<void> {
3430
+ return this.widget.populateTextContextMenu(contextMenu, textNode);
3431
+ }
3432
+ populateTagContextMenu(contextMenu: UI.ContextMenu.ContextMenu, event: Event): Promise<void> {
3433
+ return this.widget.populateTagContextMenu(contextMenu, event);
3434
+ }
3435
+ populateNodeContextMenu(contextMenu: UI.ContextMenu.ContextMenu): Promise<void> {
3436
+ return this.widget.populateNodeContextMenu(contextMenu);
3437
+ }
3438
+ populatePseudoElementContextMenu(contextMenu: UI.ContextMenu.ContextMenu): void {
3439
+ this.widget.populatePseudoElementContextMenu(contextMenu);
3440
+ }
3441
+ populateProcessingElementContextMenu(contextMenu: UI.ContextMenu.ContextMenu): Promise<void> {
3442
+ return this.widget.populateProcessingElementContextMenu(contextMenu);
3443
+ }
3444
+ hasEditableNode(): boolean {
3445
+ return this.widget.hasEditableNode();
3446
+ }
3447
+ toggleEditAsHTML(callback?: ((arg0: boolean) => void), startEditing?: boolean): void {
3448
+ this.widget.toggleEditAsHTML(callback, startEditing);
3449
+ }
3450
+
3451
+ get expandAllButtonElement(): UI.TreeOutline.TreeElement|null {
3452
+ return this.widget.expandAllButtonElement;
3453
+ }
3454
+ set expandAllButtonElement(element: UI.TreeOutline.TreeElement|null) {
3455
+ this.widget.expandAllButtonElement = element;
3456
+ }
3457
+
3458
+ node(): SDK.DOMModel.DOMNode {
3459
+ return this.nodeInternal;
3460
+ }
3461
+
3462
+ isClosingTag(): boolean {
3463
+ return this.tagTypeContext.tagType === TagType.CLOSING;
3464
+ }
3465
+
3466
+ override onattach(): void {
3467
+ this.widget.onattach();
3468
+ }
3469
+
3470
+ override async onpopulate(): Promise<void> {
3471
+ return await this.widget.onpopulate();
3472
+ }
3473
+
3474
+ override async expandRecursively(): Promise<void> {
3475
+ await this.nodeInternal.getSubtree(100, true);
3476
+ await super.expandRecursively(Number.MAX_VALUE);
3477
+ }
3478
+
3479
+ override onexpand(): void {
3480
+ this.widget.onexpand();
3481
+ }
3482
+
3483
+ override oncollapse(): void {
3484
+ this.widget.oncollapse();
3485
+ }
3486
+
3487
+ override select(omitFocus?: boolean, selectedByUser?: boolean): boolean {
3488
+ if (this.widget.editing) {
3489
+ return false;
3490
+ }
3491
+ return super.select(omitFocus, selectedByUser);
3492
+ }
3493
+
3494
+ override onselect(selectedByUser?: boolean): boolean {
3495
+ return this.widget.onselect(selectedByUser);
3496
+ }
3497
+
3498
+ override ondelete(): boolean {
3499
+ return this.widget.ondelete();
3500
+ }
3501
+
3502
+ override onenter(): boolean {
3503
+ return this.widget.onenter();
3504
+ }
3505
+
3506
+ override selectOnMouseDown(event: MouseEvent): void {
3507
+ super.selectOnMouseDown(event);
3508
+
3509
+ if (this.widget.editing) {
3365
3510
  return;
3366
3511
  }
3367
- try {
3368
- if (constructorObject.type === 'function') {
3369
- const functionDetails =
3370
- await SDK.RemoteObject.RemoteFunction.objectAsFunction(constructorObject).targetFunctionDetails();
3371
- if (functionDetails?.location) {
3372
- const uiLocation =
3373
- await Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding.instance().rawLocationToUILocation(
3374
- functionDetails.location);
3375
- if (uiLocation) {
3376
- void Common.Revealer.reveal(uiLocation);
3377
- }
3378
- }
3379
- }
3380
- } finally {
3381
- constructorObject.release();
3512
+
3513
+ // Prevent selecting the nearest word on double click.
3514
+ if (event.detail >= 2) {
3515
+ event.preventDefault();
3382
3516
  }
3383
3517
  }
3518
+
3519
+ override ondblclick(event: Event): boolean {
3520
+ return this.widget.ondblclick(event);
3521
+ }
3522
+
3523
+ override onbind(): void {
3524
+ this.widget.onbind();
3525
+ }
3526
+
3527
+ override onunbind(): void {
3528
+ this.widget.onunbind();
3529
+ }
3530
+
3531
+ static animateOnDOMUpdate(treeElement: ElementsTreeElement): void {
3532
+ ElementsTreeWidget.animateOnDOMUpdate(treeElement);
3533
+ }
3534
+
3535
+ static visibleShadowRoots(node: SDK.DOMModel.DOMNode): SDK.DOMModel.DOMNode[] {
3536
+ return ElementsTreeWidget.visibleShadowRoots(node);
3537
+ }
3538
+
3539
+ static canShowInlineText(node: SDK.DOMModel.DOMNode): boolean {
3540
+ return ElementsTreeWidget.canShowInlineText(node);
3541
+ }
3542
+
3543
+ static populateForcedPseudoStateItems(contextMenu: UI.ContextMenu.ContextMenu, node: SDK.DOMModel.DOMNode): void {
3544
+ ElementsTreeWidget.populateForcedPseudoStateItems(contextMenu, node);
3545
+ }
3546
+
3547
+ set hovered(isHovered: boolean) {
3548
+ this.widget.hovered = isHovered;
3549
+ if (this.listItemElement) {
3550
+ this.listItemElement.classList.toggle('hovered', isHovered);
3551
+ }
3552
+ }
3553
+
3554
+ get hovered(): boolean {
3555
+ return this.widget.hovered;
3556
+ }
3557
+
3558
+ updateAdorners(): void {
3559
+ this.widget.updateAdorners();
3560
+ }
3561
+
3562
+ get updateComplete(): Promise<void> {
3563
+ return this.widget.updateComplete;
3564
+ }
3565
+
3566
+ requestUpdate(): void {
3567
+ this.widget.requestUpdate();
3568
+ }
3569
+ updateTitle(updateRecord?: Elements.ElementUpdateRecord.ElementUpdateRecord|null): void {
3570
+ this.widget.updateTitle(updateRecord);
3571
+ }
3572
+
3573
+ triggerEditAttribute(attributeName: string): boolean {
3574
+ return this.widget.triggerEditAttribute(attributeName) || false;
3575
+ }
3576
+
3577
+ editingCancelled(element: Element, tagName: string|null): void {
3578
+ this.widget.editingCancelled(element, tagName);
3579
+ }
3580
+
3581
+ updateDecorations(): void {
3582
+ this.widget.updateDecorations();
3583
+ }
3584
+
3585
+ async remove(): Promise<void> {
3586
+ return await this.widget.remove();
3587
+ }
3588
+
3589
+ addNewAttribute(): boolean {
3590
+ return this.widget.addNewAttribute();
3591
+ }
3592
+ startEditingTagName(tagNameElement?: Element): boolean {
3593
+ return this.widget.startEditingTagName(tagNameElement);
3594
+ }
3595
+
3596
+ startEditingProcessingInstructionValue(): boolean|undefined {
3597
+ return this.widget.startEditingProcessingInstructionValue();
3598
+ }
3599
+
3600
+ isDisplayContents(): boolean {
3601
+ return this.widget.isDisplayContents();
3602
+ }
3603
+
3604
+ performUpdate(): void {
3605
+ this.widget.performUpdate();
3606
+ }
3384
3607
  }
3385
3608
 
3386
3609
  export const InitialChildrenLimit = 500;