chrome-devtools-frontend 1.0.1701636 → 1.0.1703574

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 (91) hide show
  1. package/eslint.config.mjs +3 -1
  2. package/front_end/core/common/ParsedURL.ts +5 -1
  3. package/front_end/core/host/UserMetricsEnums.ts +2 -1
  4. package/front_end/core/root/ExperimentNames.ts +1 -0
  5. package/front_end/core/sdk/CSSPropertyParserMatchers.ts +2 -7
  6. package/front_end/core/text_utils/Markdown.ts +73 -0
  7. package/front_end/core/text_utils/text_utils.ts +2 -0
  8. package/front_end/devtools_compatibility.js +2 -1
  9. package/front_end/entrypoints/main/MainImpl.ts +8 -0
  10. package/front_end/generated/InspectorBackendCommands.ts +6 -4
  11. package/front_end/generated/protocol-mapping.d.ts +8 -0
  12. package/front_end/generated/protocol-proxy-api.d.ts +6 -0
  13. package/front_end/generated/protocol.ts +22 -3
  14. package/front_end/models/ai_assistance/AiAgent2.ts +13 -1
  15. package/front_end/models/ai_assistance/AiConversation.ts +1 -1
  16. package/front_end/models/ai_assistance/agents/ContextSelectionAgent.ts +13 -19
  17. package/front_end/models/change_tracker/ChangeTracker.ts +5 -64
  18. package/front_end/models/comment_manager/CD4ABridge.ts +5 -0
  19. package/front_end/models/comment_manager/CommentManager.ts +3 -5
  20. package/front_end/models/comment_manager/CommentThread.ts +30 -13
  21. package/front_end/models/issues_manager/CookieDeprecationMetadataIssue.ts +6 -7
  22. package/front_end/models/issues_manager/DeprecationIssue.ts +1 -1
  23. package/front_end/models/issues_manager/MarkdownIssueDescription.ts +18 -52
  24. package/front_end/models/issues_manager/SRIMessageSignatureIssue.ts +2 -4
  25. package/front_end/models/issues_manager/descriptions/cookieWarnMetadataGrantRead.md +0 -1
  26. package/front_end/models/issues_manager/descriptions/cookieWarnMetadataGrantReadOptOut.md +5 -0
  27. package/front_end/models/issues_manager/descriptions/cookieWarnMetadataGrantSet.md +0 -1
  28. package/front_end/models/issues_manager/descriptions/cookieWarnMetadataGrantSetOptOut.md +5 -0
  29. package/front_end/models/issues_manager/descriptions/deprecation.md +1 -1
  30. package/front_end/models/issues_manager/descriptions/sriValidationFailedIntegrityMismatch.md +2 -0
  31. package/front_end/models/persistence/AutomaticFileSystemWorkspaceBinding.ts +1 -1
  32. package/front_end/models/persistence/NetworkPersistenceManager.ts +14 -2
  33. package/front_end/panels/common/CommentThreadWidget.ts +29 -10
  34. package/front_end/panels/common/CommentsOverlayWidget.ts +50 -14
  35. package/front_end/panels/common/CommentsStatusBarPill.ts +40 -7
  36. package/front_end/panels/common/commentThreadWidget.css +11 -0
  37. package/front_end/panels/common/commentsStatusBarPill.css +13 -0
  38. package/front_end/panels/elements/StylePropertiesSection.ts +10 -0
  39. package/front_end/panels/elements/StylePropertyTreeElement.ts +1 -1
  40. package/front_end/panels/elements/StylesSidebarPane.ts +101 -49
  41. package/front_end/panels/elements/components/CSSQuery.ts +1 -1
  42. package/front_end/panels/elements/components/cssQuery.css +4 -0
  43. package/front_end/panels/elements/elements.ts +0 -1
  44. package/front_end/panels/elements/stylesSidebarPane.css +6 -0
  45. package/front_end/panels/issues/IssueView.ts +5 -1
  46. package/front_end/panels/sources/ResourceOriginPlugin.ts +8 -3
  47. package/front_end/panels/sources/ScopeChainSidebarPane.ts +3 -4
  48. package/front_end/panels/sources/SourcesView.ts +127 -147
  49. package/front_end/panels/sources/TabbedEditorContainer.ts +518 -246
  50. package/front_end/panels/sources/UISourceCodeFrame.ts +9 -0
  51. package/front_end/third_party/chromium/README.chromium +1 -1
  52. package/front_end/third_party/chromium/client-variations/ClientVariations.js +25 -96
  53. package/front_end/third_party/codemirror.next/chunk/angular.js +1 -1
  54. package/front_end/third_party/codemirror.next/chunk/codemirror.js +1 -1
  55. package/front_end/third_party/codemirror.next/chunk/codemirror.js.map +1 -1
  56. package/front_end/third_party/codemirror.next/chunk/cpp.js +1 -1
  57. package/front_end/third_party/codemirror.next/chunk/cpp.js.map +1 -1
  58. package/front_end/third_party/codemirror.next/chunk/java.js +1 -1
  59. package/front_end/third_party/codemirror.next/chunk/java.js.map +1 -1
  60. package/front_end/third_party/codemirror.next/chunk/legacy.js +1 -1
  61. package/front_end/third_party/codemirror.next/chunk/less.js +1 -1
  62. package/front_end/third_party/codemirror.next/chunk/less.js.map +1 -1
  63. package/front_end/third_party/codemirror.next/chunk/markdown.js +1 -1
  64. package/front_end/third_party/codemirror.next/chunk/markdown.js.map +1 -1
  65. package/front_end/third_party/codemirror.next/chunk/php.js +1 -1
  66. package/front_end/third_party/codemirror.next/chunk/php.js.map +1 -1
  67. package/front_end/third_party/codemirror.next/chunk/python.js +1 -1
  68. package/front_end/third_party/codemirror.next/chunk/python.js.map +1 -1
  69. package/front_end/third_party/codemirror.next/chunk/sass.js +1 -1
  70. package/front_end/third_party/codemirror.next/chunk/sass.js.map +1 -1
  71. package/front_end/third_party/codemirror.next/chunk/svelte.js +1 -1
  72. package/front_end/third_party/codemirror.next/chunk/svelte.js.map +1 -1
  73. package/front_end/third_party/codemirror.next/chunk/vue.js +1 -1
  74. package/front_end/third_party/codemirror.next/chunk/wast.js +1 -1
  75. package/front_end/third_party/codemirror.next/chunk/wast.js.map +1 -1
  76. package/front_end/third_party/codemirror.next/chunk/xml.js +1 -1
  77. package/front_end/third_party/codemirror.next/chunk/xml.js.map +1 -1
  78. package/front_end/third_party/codemirror.next/codemirror.next.js +1 -1
  79. package/front_end/third_party/codemirror.next/package.json +4 -1
  80. package/front_end/third_party/codemirror.next/rebuild.sh +1 -1
  81. package/front_end/ui/comments/CommentOverlayManager.ts +11 -10
  82. package/front_end/ui/components/markdown_view/MarkdownPlaceholderLitRenderer.ts +59 -0
  83. package/front_end/ui/components/markdown_view/markdown_view.ts +2 -0
  84. package/front_end/ui/legacy/TabbedPane.ts +51 -40
  85. package/front_end/ui/legacy/Treeoutline.ts +28 -19
  86. package/front_end/ui/legacy/UIUtils.ts +1 -1
  87. package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +424 -570
  88. package/front_end/ui/legacy/components/object_ui/objectPropertiesSection.css +2 -27
  89. package/front_end/ui/visual_logging/KnownContextValues.ts +1 -0
  90. package/mcp/mcp.ts +1 -0
  91. package/package.json +2 -1
@@ -1,7 +1,6 @@
1
1
  // Copyright 2020 The Chromium Authors
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
- /* eslint-disable @devtools/no-imperative-dom-api */
5
4
 
6
5
  /*
7
6
  * Copyright (C) 2008 Apple Inc. All Rights Reserved.
@@ -66,6 +65,7 @@ export interface ObjectPropertySearchResult extends UI.TreeOutline.TreeSearchRes
66
65
  range: TextUtils.TextRange.SourceRange;
67
66
  }
68
67
  const {widget, widgetRef} = UI.Widget;
68
+ const {ifExpanded} = UI.TreeOutline;
69
69
  const {ref, repeat, ifDefined, classMap} = Directives;
70
70
  const UIStrings = {
71
71
  /**
@@ -147,21 +147,12 @@ const UIStrings = {
147
147
  /**
148
148
  * @description Tooltip text for the button to open a memory buffer object in the Memory inspector panel.
149
149
  */
150
- openInMemoryInpector: 'Open in Memory inspector panel',
150
+ openInMemoryInspector: 'Open in Memory inspector panel',
151
151
  } as const;
152
152
  const str_ = i18n.i18n.registerUIStrings('ui/legacy/components/object_ui/ObjectPropertiesSection.ts', UIStrings);
153
153
  const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
154
154
  export const EXPANDABLE_MAX_DEPTH = 100;
155
155
 
156
- // TODO(crbug.com/457388389): This cache is a temporary workaround for the <devtools-tree> migration.
157
- // It can be removed once the entire ObjectPropertiesSection is fully migrated to Lit and
158
- // the legacy TreeOutline/TreeElement dependencies are removed.
159
- const topLevelNodesCache = new WeakMap<ObjectTreeNodeBase, {
160
- nodes: UI.TreeOutline.TreeElement[],
161
- listener: () => void,
162
- linkifier?: Components.Linkifier.Linkifier,
163
- }>();
164
-
165
156
  interface NodeChildren {
166
157
  properties?: ObjectTreeNode[];
167
158
  internalProperties?: ObjectTreeNode[];
@@ -392,6 +383,139 @@ const ARRAY_LOAD_THRESHOLD = 100;
392
383
  const ARRAY_BUCKET_THRESHOLD = 100;
393
384
  const ARRAY_SPARSE_ITERATION_THRESHOLD = 250000;
394
385
 
386
+ async function arrayRangeGroups(object: SDK.RemoteObject.RemoteObject, fromIndex: number,
387
+ toIndex: number): Promise<{ranges: number[][]}|null|undefined> {
388
+ return await object.callFunctionJSON(packArrayRanges, [
389
+ {value: fromIndex},
390
+ {value: toIndex},
391
+ {value: ARRAY_BUCKET_THRESHOLD},
392
+ {value: ARRAY_SPARSE_ITERATION_THRESHOLD},
393
+ ]);
394
+
395
+ /**
396
+ * This function is called on the RemoteObject.
397
+ * Note: must declare params as optional.
398
+ */
399
+ function packArrayRanges(this: Object, fromIndex?: number, toIndex?: number, bucketThreshold?: number,
400
+ sparseIterationThreshold?: number): {
401
+ ranges: number[][],
402
+ }|undefined {
403
+ if (fromIndex === undefined || toIndex === undefined || sparseIterationThreshold === undefined ||
404
+ bucketThreshold === undefined) {
405
+ return;
406
+ }
407
+ let ownPropertyNames: string[]|null = null;
408
+ const consecutiveRange = (toIndex - fromIndex >= sparseIterationThreshold) && ArrayBuffer.isView(this);
409
+
410
+ function* arrayIndexes(object: Object): Generator<number, void, unknown> {
411
+ if (fromIndex === undefined || toIndex === undefined || sparseIterationThreshold === undefined) {
412
+ return;
413
+ }
414
+
415
+ if (toIndex - fromIndex < sparseIterationThreshold) {
416
+ for (let i = fromIndex; i <= toIndex; ++i) {
417
+ if (i in object) {
418
+ yield i;
419
+ }
420
+ }
421
+ } else {
422
+ ownPropertyNames = ownPropertyNames || Object.getOwnPropertyNames(object);
423
+ for (let i = 0; i < ownPropertyNames.length; ++i) {
424
+ const name = ownPropertyNames[i];
425
+
426
+ const index = Number(name) >>> 0;
427
+ if ((String(index)) === name && fromIndex <= index && index <= toIndex) {
428
+ yield index;
429
+ }
430
+ }
431
+ }
432
+ }
433
+
434
+ let count = 0;
435
+ if (consecutiveRange) {
436
+ count = toIndex - fromIndex + 1;
437
+ } else {
438
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
439
+ for (const ignored of arrayIndexes(this)) {
440
+ ++count;
441
+ }
442
+ }
443
+
444
+ let bucketSize: number = count;
445
+ if (count <= bucketThreshold) {
446
+ bucketSize = count;
447
+ } else {
448
+ bucketSize = Math.pow(bucketThreshold, Math.ceil(Math.log(count) / Math.log(bucketThreshold)) - 1);
449
+ }
450
+
451
+ const ranges = [];
452
+ if (consecutiveRange) {
453
+ for (let i = fromIndex; i <= toIndex; i += bucketSize) {
454
+ const groupStart = i;
455
+ let groupEnd: number = groupStart + bucketSize - 1;
456
+ if (groupEnd > toIndex) {
457
+ groupEnd = toIndex;
458
+ }
459
+ ranges.push([groupStart, groupEnd, groupEnd - groupStart + 1]);
460
+ }
461
+ } else {
462
+ count = 0;
463
+ let groupStart = -1;
464
+ let groupEnd = 0;
465
+ for (const i of arrayIndexes(this)) {
466
+ if (groupStart === -1) {
467
+ groupStart = i;
468
+ }
469
+ groupEnd = i;
470
+ if (++count === bucketSize) {
471
+ ranges.push([groupStart, groupEnd, count]);
472
+ count = 0;
473
+ groupStart = -1;
474
+ }
475
+ }
476
+ if (count > 0) {
477
+ ranges.push([groupStart, groupEnd, count]);
478
+ }
479
+ }
480
+
481
+ return {ranges};
482
+ }
483
+ }
484
+
485
+ /**
486
+ * This function is called on the RemoteObject.
487
+ */
488
+ function buildArrayFragment(
489
+ this: Record<number, Object>,
490
+ fromIndex?: number,
491
+ toIndex?: number,
492
+ sparseIterationThreshold?: number,
493
+ ): unknown {
494
+ const result = Object.create(null);
495
+
496
+ if (fromIndex === undefined || toIndex === undefined || sparseIterationThreshold === undefined) {
497
+ return;
498
+ }
499
+
500
+ if (toIndex - fromIndex < sparseIterationThreshold) {
501
+ for (let i = fromIndex; i <= toIndex; ++i) {
502
+ if (i in this) {
503
+ result[i] = this[i];
504
+ }
505
+ }
506
+ } else {
507
+ const ownPropertyNames = Object.getOwnPropertyNames(this);
508
+ for (let i = 0; i < ownPropertyNames.length; ++i) {
509
+ const name = ownPropertyNames[i];
510
+ const index = Number(name) >>> 0;
511
+ if (String(index) === name && fromIndex <= index && index <= toIndex) {
512
+ result[index] = this[index];
513
+ }
514
+ }
515
+ }
516
+ return result;
517
+ }
518
+
395
519
  export abstract class ObjectTreeNodeBase extends Common.ObjectWrapper.ObjectWrapper<ObjectTreeNodeBase.EventTypes> {
396
520
  #children?: NodeChildren;
397
521
  protected readonly options: ObjectTreeOptions;
@@ -502,8 +626,6 @@ export abstract class ObjectTreeNodeBase extends Common.ObjectWrapper.ObjectWrap
502
626
  }
503
627
 
504
628
  setFilter(filter: {includeNullOrUndefinedValues: boolean, regex: RegExp|null}|null): void {
505
- this.filter = filter;
506
- this.dispatchEventToListeners(ObjectTreeNodeBase.Events.FILTER_CHANGED);
507
629
  this.#walk().forEach(c => {
508
630
  c.filter = filter;
509
631
  c.dispatchEventToListeners(ObjectTreeNodeBase.Events.FILTER_CHANGED);
@@ -766,11 +888,11 @@ class ArrayGroupTreeNode extends ObjectTreeNodeBase {
766
888
 
767
889
  export class ObjectTreeNode extends ObjectTreeNodeBase {
768
890
  #path?: string;
891
+ showAllChildren: boolean = false;
769
892
  constructor(
770
893
  readonly property: SDK.RemoteObject.RemoteObjectProperty,
771
894
  parent: ObjectTreeNodeBase|undefined,
772
895
  options: ObjectTreeOptions,
773
- readonly nonSyntheticParent?: SDK.RemoteObject.RemoteObject|undefined,
774
896
  ) {
775
897
  super(parent, options);
776
898
  }
@@ -1084,8 +1206,8 @@ export function getMemoryIcon(object: SDK.RemoteObject.RemoteObject, expression?
1084
1206
  void Common.Revealer.reveal(new SDK.RemoteObject.LinearMemoryInspectable(object, expression));
1085
1207
  }}
1086
1208
  jslog=${VisualLogging.action('open-memory-inspector').track({click: true})}
1087
- title=${i18nString(UIStrings.openInMemoryInpector)}
1088
- aria-label=${i18nString(UIStrings.openInMemoryInpector)}></devtools-icon>`;
1209
+ title=${i18nString(UIStrings.openInMemoryInspector)}
1210
+ aria-label=${i18nString(UIStrings.openInMemoryInspector)}></devtools-icon>`;
1089
1211
  // clang-format on
1090
1212
  }
1091
1213
 
@@ -1253,13 +1375,16 @@ export const enum ObjectPropertiesMode {
1253
1375
  OWN_AND_INTERNAL_AND_INHERITED = 1, // Own, internal, and inherited properties
1254
1376
  }
1255
1377
 
1378
+ export interface ContextMenuHandlers {
1379
+ expandRecursively: (node: ObjectTreeNodeBase) => void;
1380
+ collapseChildren: (node: ObjectTreeNodeBase) => void;
1381
+ sortPropertiesAlphabetically: (node: ObjectTreeNodeBase) => void;
1382
+ onShowAllToggled: (node: ObjectTreeNodeBase) => void;
1383
+ }
1384
+
1256
1385
  export function populateObjectTreeContextMenu(contextMenu: UI.ContextMenu.ContextMenu,
1257
- objectOrProperty: ObjectTree|ObjectTreeNode, handlers: {
1258
- expandRecursively: (node: ObjectTreeNodeBase) => void,
1259
- collapseChildren: (node: ObjectTreeNodeBase) => void,
1260
- sortPropertiesAlphabetically: (node: ObjectTreeNodeBase) => void,
1261
- onShowAllToggled: (node: ObjectTreeNodeBase) => void,
1262
- }): void {
1386
+ objectOrProperty: ObjectTree|ObjectTreeNode,
1387
+ handlers: ContextMenuHandlers): void {
1263
1388
  contextMenu.appendApplicableItems(objectOrProperty);
1264
1389
  if (objectOrProperty instanceof ObjectTreeNode && objectOrProperty.property.symbol) {
1265
1390
  contextMenu.appendApplicableItems(objectOrProperty.property.symbol);
@@ -1314,14 +1439,15 @@ export function populateObjectTreeContextMenu(contextMenu: UI.ContextMenu.Contex
1314
1439
  {checked: objectOrProperty.includeNullOrUndefinedValues, jslogContext: 'show-all'});
1315
1440
  }
1316
1441
 
1317
- interface ObjectTreeViewInput {
1442
+ interface ObjectTreeViewInput extends ContextMenuHandlers {
1443
+ onShowAllProperties: (node: ObjectTreeNode) => void;
1444
+ onExpand: (node: ObjectTreeNodeBase, expanded: boolean) => void;
1318
1445
  renderAsSubtree: boolean;
1319
1446
  objectTree?: ObjectTree;
1320
1447
  linkifier?: Components.Linkifier.Linkifier;
1321
- emptyPlaceholder?: string;
1448
+ emptyPlaceholder?: LitTemplate;
1322
1449
  skipProto: boolean;
1323
1450
  skipGettersAndSetters: boolean;
1324
- onExpand: (expanded: boolean) => void;
1325
1451
  }
1326
1452
  type ObjectTreeView = (input: ObjectTreeViewInput, output: object, target: HTMLElement) => void;
1327
1453
  const OBJECT_TREE_DEFAULT_VIEW: ObjectTreeView = (input, output, target) => {
@@ -1330,50 +1456,178 @@ const OBJECT_TREE_DEFAULT_VIEW: ObjectTreeView = (input, output, target) => {
1330
1456
  render(nothing, target);
1331
1457
  return;
1332
1458
  }
1333
- const classes = input.renderAsSubtree ? ['source-code', 'object-properties-section'] : [];
1334
- let entry = topLevelNodesCache.get(objectTree);
1335
- if (!entry || entry.linkifier !== input.linkifier || (!entry.nodes.length && objectTree.children)) {
1336
- if (entry) {
1337
- objectTree.removeEventListener(ObjectTreeNodeBase.Events.CHILDREN_CHANGED, entry.listener);
1338
- }
1339
- const nodes = Array.from(ObjectPropertyTreeElement.createNodes(
1340
- objectTree, input.skipProto, input.skipGettersAndSetters, input.linkifier, input.emptyPlaceholder));
1341
- const listener = (): void => {
1342
- topLevelNodesCache.delete(objectTree);
1343
- objectTree.removeEventListener(ObjectTreeNodeBase.Events.CHILDREN_CHANGED, listener);
1459
+
1460
+ function renderProperty(property: ObjectTreeNode): LitTemplate {
1461
+ const onContextMenu = (e: Event): void => {
1462
+ const contextMenu = new UI.ContextMenu.ContextMenu(e);
1463
+ populateObjectTreeContextMenu(contextMenu, property, input);
1464
+ void contextMenu.show();
1344
1465
  };
1345
- entry = {linkifier: input.linkifier, nodes, listener};
1346
- topLevelNodesCache.set(objectTree, entry);
1347
- objectTree.addEventListener(ObjectTreeNodeBase.Events.CHILDREN_CHANGED, listener);
1348
- }
1349
1466
 
1350
- render(entry.nodes.map(node => html`<devtools-tree-wrapper .treeElement=${node}></devtools-tree-wrapper>`), target, {
1351
- container: {
1352
- classes,
1353
- interceptedListeners: {
1354
- expand: (e: Event) => input.onExpand((e as UI.TreeOutline.TreeViewElement.ExpandEvent).detail.expanded),
1355
- },
1356
- },
1357
- });
1358
- };
1467
+ const onExpand = (e: UI.TreeOutline.TreeViewElement.ExpandEvent): void => {
1468
+ input.onExpand(property, e.detail.expanded);
1469
+ e.consume(true);
1470
+ };
1471
+ const childCount = (property.children?.properties?.length ?? 0) +
1472
+ (property.children?.internalProperties?.length ?? 0) + (property.children?.accessors?.length ?? 0) +
1473
+ (property.children?.arrayRanges?.length ?? 0);
1474
+
1475
+ const showAllChildren = childCount <= InitialVisibleChildrenLimit || property.showAllChildren;
1476
+ const children = (): Iterable<LitTemplate> => {
1477
+ const nodes = createNodes(property, false, false, undefined, p => !isDisplayableProperty(p, property.property));
1478
+ return showAllChildren ? nodes : nodes.take(InitialVisibleChildrenLimit);
1479
+ };
1359
1480
 
1360
- async function populateChildrenIfNeeded(node: ObjectTreeNodeBase): Promise<void> {
1361
- const children = await node.populateChildrenIfNeeded();
1362
- if (!children.arrayRanges) {
1363
- return;
1481
+ const expandable = property.object && !property.object.customPreview() && property.object.hasChildren &&
1482
+ !property.property.wasThrown;
1483
+
1484
+ // clang-format off
1485
+ return html`<li
1486
+ ?hidden=${property.isFiltered}
1487
+ ?open=${property.expanded}
1488
+ @contextmenu=${onContextMenu}
1489
+ @expand=${onExpand}
1490
+ data-object-property-name-for-test=${property.name}
1491
+ toggle-on-click
1492
+ role=treeitem>
1493
+ ${widget(ObjectPropertyWidget, {property, linkifier: input.linkifier, editable: !property.readOnly, expanded: property.expanded})}
1494
+ ${expandable ? html`
1495
+ <ul role=group>
1496
+ ${children()}
1497
+ ${showAllChildren ? nothing : html`
1498
+ <li role=treeitem @select=${() => input.onShowAllProperties(property)}>
1499
+ <div
1500
+ class=object-value-calculate-value-button
1501
+ title=${i18nString(UIStrings.showAllD, {PH1: childCount})}>
1502
+ ${i18nString(UIStrings.dots)}
1503
+ </div>
1504
+ </li>`}
1505
+ </ul>` : nothing}
1506
+ </li>`;
1507
+ // clang-format on
1508
+ }
1509
+
1510
+ function renderArrayGroup(child: ArrayGroupTreeNode): LitTemplate {
1511
+ const onExpand = (e: UI.TreeOutline.TreeViewElement.ExpandEvent): void => {
1512
+ input.onExpand(child, e.detail.expanded);
1513
+ e.consume(true);
1514
+ };
1515
+
1516
+ // clang-format off
1517
+ return html`<li
1518
+ class=object-properties-section-name
1519
+ role=treeitem
1520
+ toggle-on-click
1521
+ ?open=${child.expanded}
1522
+ @expand=${onExpand}>
1523
+ ${Platform.StringUtilities.sprintf('[%d … %d]', child.range.fromIndex, child.range.toIndex)}
1524
+ <ul role=group>
1525
+ ${ifExpanded(() => createNodes(child, false, false))}
1526
+ </ul>
1527
+ </li>`;
1528
+ // clang-format on
1529
+ }
1530
+
1531
+ function*
1532
+ createNodes(
1533
+ value: ObjectTreeNodeBase,
1534
+ skipProto: boolean,
1535
+ skipGettersAndSetters: boolean,
1536
+ emptyPlaceholder?: LitTemplate,
1537
+ isNotDisplayablePropertyCallback?: (property: SDK.RemoteObject.RemoteObjectProperty) => boolean,
1538
+ ): Generator<LitTemplate> {
1539
+ const properties = value.children;
1540
+ if (!properties) {
1541
+ return;
1542
+ }
1543
+ if (properties.arrayRanges) {
1544
+ if (properties.arrayRanges.length === 1) {
1545
+ yield* createNodes(properties.arrayRanges[0], false, false, undefined, isNotDisplayablePropertyCallback);
1546
+ } else {
1547
+ for (const child of properties.arrayRanges) {
1548
+ if (child.singular) {
1549
+ yield* createNodes(child, false, false, undefined, isNotDisplayablePropertyCallback);
1550
+ } else {
1551
+ yield renderArrayGroup(child);
1552
+ }
1553
+ }
1554
+ }
1555
+
1556
+ yield* createPropertyNodes(properties, false, false, undefined, isNotDisplayablePropertyCallback);
1557
+ } else {
1558
+ yield*
1559
+ createPropertyNodes(properties, skipProto, skipGettersAndSetters, emptyPlaceholder,
1560
+ isNotDisplayablePropertyCallback);
1561
+ }
1364
1562
  }
1365
- if (children.arrayRanges.length === 1) {
1366
- await populateChildrenIfNeeded(children.arrayRanges[0]);
1367
- } else {
1368
- await Promise.all(
1369
- children.arrayRanges.filter(child => child.singular).map(child => populateChildrenIfNeeded(child)));
1563
+
1564
+ function*
1565
+ createPropertyNodes({properties, internalProperties, accessors, arrayRanges}: NodeChildren, skipProto: boolean,
1566
+ skipGettersAndSetters: boolean, emptyPlaceholder?: LitTemplate,
1567
+ isNotDisplayablePropertyCallback?: (property: SDK.RemoteObject.RemoteObjectProperty) =>
1568
+ boolean): Generator<LitTemplate> {
1569
+ let empty = true;
1570
+ // Arrays with large numbers of elements are paginated into arrayRanges.
1571
+ // If we have array ranges, the object is not empty.
1572
+ if (arrayRanges && arrayRanges.length > 0) {
1573
+ empty = false;
1574
+ }
1575
+
1576
+ const entriesProperty = internalProperties?.find(({property}) => property.name === '[[Entries]]');
1577
+ if (entriesProperty) {
1578
+ yield renderProperty(entriesProperty);
1579
+ empty = false;
1580
+ }
1581
+
1582
+ for (const property of properties ?? []) {
1583
+ if (isNotDisplayablePropertyCallback?.(property.property)) {
1584
+ continue;
1585
+ }
1586
+
1587
+ const canShowProperty = property.property.getter || !property.property.isAccessorProperty();
1588
+ if (canShowProperty) {
1589
+ yield renderProperty(property);
1590
+ empty = false;
1591
+ }
1592
+ }
1593
+
1594
+ if (!skipGettersAndSetters) {
1595
+ for (const accessor of accessors ?? []) {
1596
+ yield renderProperty(accessor);
1597
+ }
1598
+ }
1599
+
1600
+ for (const property of internalProperties ?? []) {
1601
+ if (property.property.name === '[[Entries]]') {
1602
+ continue;
1603
+ }
1604
+ if (property.property.name === '[[Prototype]]' && skipProto) {
1605
+ continue;
1606
+ }
1607
+ yield renderProperty(property);
1608
+ empty = false;
1609
+ }
1610
+
1611
+ if (empty) {
1612
+ yield html`<li role=treeitem>
1613
+ <div class=gray-info-message>${emptyPlaceholder || i18nString(UIStrings.noProperties)}</div>
1614
+ </li>`;
1615
+ }
1370
1616
  }
1371
- }
1617
+
1618
+ const nodes =
1619
+ Array.from(createNodes(objectTree, input.skipProto, input.skipGettersAndSetters, input.emptyPlaceholder));
1620
+ nodes.forEach(UI.UIUtils.HTMLElementWithLightDOMTemplate.patchLitTemplate);
1621
+
1622
+ render(nodes, target, {
1623
+ container: {classes: input.renderAsSubtree ? ['source-code', 'object-properties-section'] : []},
1624
+ });
1625
+ };
1372
1626
 
1373
1627
  export class ObjectTreeWidget extends UI.Widget.Widget {
1374
1628
  #objectTree: ObjectTree|undefined = undefined;
1375
1629
  #linkifier: Components.Linkifier.Linkifier|undefined = undefined;
1376
- #emptyPlaceholder?: string;
1630
+ #emptyPlaceholder?: LitTemplate;
1377
1631
  #renderAsSubtree = false;
1378
1632
  #skipProto = false;
1379
1633
  #skipGettersAndSetters = false;
@@ -1384,9 +1638,77 @@ export class ObjectTreeWidget extends UI.Widget.Widget {
1384
1638
  this.#view = view;
1385
1639
  }
1386
1640
 
1387
- onExpand = (expanded: boolean): void => {
1388
- if (this.#objectTree) {
1389
- this.#objectTree.expanded = expanded;
1641
+ async #populateChildrenIfNeeded(node: ObjectTreeNodeBase): Promise<void> {
1642
+ const isFirstPopulation = !node.children;
1643
+ const children = await node.populateChildrenIfNeeded();
1644
+
1645
+ if (isFirstPopulation) {
1646
+ const entriesProperty = children.internalProperties?.find(({property}) => property.name === '[[Entries]]');
1647
+ if (entriesProperty) {
1648
+ entriesProperty.expanded = true;
1649
+ }
1650
+
1651
+ for (const property of children.properties ?? []) {
1652
+ const canShowProperty = property.property.getter || !property.property.isAccessorProperty();
1653
+ if (canShowProperty) {
1654
+ if (property.property.name === 'memories' && property.object?.className === 'Memories') {
1655
+ property.expanded = true;
1656
+ }
1657
+ }
1658
+ }
1659
+ }
1660
+
1661
+ for (const child of node.treeNodeChildren()) {
1662
+ child.removeEventListener(ObjectTreeNodeBase.Events.FILTER_CHANGED, this.requestUpdate, this);
1663
+ child.removeEventListener(ObjectTreeNodeBase.Events.EXPANDED_CHANGED, this.requestUpdate, this);
1664
+ child.removeEventListener(ObjectTreeNodeBase.Events.VALUE_CHANGED, this.requestUpdate, this);
1665
+ child.removeEventListener(ObjectTreeNodeBase.Events.CHILDREN_CHANGED, this.requestUpdate, this);
1666
+ child.addEventListener(ObjectTreeNodeBase.Events.FILTER_CHANGED, this.requestUpdate, this);
1667
+ child.addEventListener(ObjectTreeNodeBase.Events.EXPANDED_CHANGED, this.requestUpdate, this);
1668
+ child.addEventListener(ObjectTreeNodeBase.Events.VALUE_CHANGED, this.requestUpdate, this);
1669
+ child.addEventListener(ObjectTreeNodeBase.Events.CHILDREN_CHANGED, this.requestUpdate, this);
1670
+ if (child.expanded) {
1671
+ await this.#populateChildrenIfNeeded(child);
1672
+ }
1673
+ }
1674
+
1675
+ if (!children.arrayRanges) {
1676
+ return;
1677
+ }
1678
+ if (children.arrayRanges.length === 1) {
1679
+ await this.#populateChildrenIfNeeded(children.arrayRanges[0]);
1680
+ } else {
1681
+ await Promise.all(
1682
+ children.arrayRanges.filter(child => child.singular).map(child => this.#populateChildrenIfNeeded(child)));
1683
+ }
1684
+ }
1685
+
1686
+ onExpand = (node: ObjectTreeNodeBase, expanded: boolean): void => {
1687
+ if (node.expanded !== expanded) {
1688
+ node.expanded = expanded;
1689
+ this.requestUpdate();
1690
+ }
1691
+ };
1692
+ expandRecursively = (node: ObjectTreeNodeBase): void => {
1693
+ void node.expandRecursively(EXPANDABLE_MAX_DEPTH);
1694
+ this.requestUpdate();
1695
+ };
1696
+ collapseChildren = (node: ObjectTreeNodeBase): void => {
1697
+ node.collapseRecursively();
1698
+ this.requestUpdate();
1699
+ };
1700
+ sortPropertiesAlphabetically = (node: ObjectTreeNodeBase): void => {
1701
+ node.sortPropertiesAlphabetically = !node.sortPropertiesAlphabetically;
1702
+ this.requestUpdate();
1703
+ };
1704
+ onShowAllToggled = (node: ObjectTreeNodeBase): void => {
1705
+ node.includeNullOrUndefinedValues = !node.includeNullOrUndefinedValues;
1706
+ this.requestUpdate();
1707
+ };
1708
+ onShowAllProperties = (node: ObjectTreeNode): void => {
1709
+ if (!node.showAllChildren) {
1710
+ node.showAllChildren = true;
1711
+ this.requestUpdate();
1390
1712
  }
1391
1713
  };
1392
1714
 
@@ -1436,11 +1758,11 @@ export class ObjectTreeWidget extends UI.Widget.Widget {
1436
1758
  this.requestUpdate();
1437
1759
  }
1438
1760
 
1439
- get emptyPlaceholder(): string|undefined {
1761
+ get emptyPlaceholder(): LitTemplate|undefined {
1440
1762
  return this.#emptyPlaceholder;
1441
1763
  }
1442
1764
 
1443
- set emptyPlaceholder(val: string) {
1765
+ set emptyPlaceholder(val: LitTemplate) {
1444
1766
  if (val === this.#emptyPlaceholder) {
1445
1767
  return;
1446
1768
  }
@@ -1462,7 +1784,7 @@ export class ObjectTreeWidget extends UI.Widget.Widget {
1462
1784
 
1463
1785
  override async performUpdate(): Promise<void> {
1464
1786
  if (this.#objectTree?.expanded) {
1465
- await populateChildrenIfNeeded(this.#objectTree);
1787
+ await this.#populateChildrenIfNeeded(this.#objectTree);
1466
1788
  }
1467
1789
  this.#view(this, {}, this.contentElement);
1468
1790
  }
@@ -1484,7 +1806,7 @@ export class ObjectTreeWidget extends UI.Widget.Widget {
1484
1806
  export function renderObjectTree(
1485
1807
  objectTree: ObjectTree,
1486
1808
  linkifier?: Components.Linkifier.Linkifier,
1487
- emptyPlaceholder?: string,
1809
+ emptyPlaceholder?: LitTemplate,
1488
1810
  ): LitTemplate|DirectiveResult {
1489
1811
  return html`<ul role="group" ${
1490
1812
  widget(ObjectTreeWidget, {objectTree, linkifier, emptyPlaceholder, renderAsSubtree: true})} ${
@@ -1840,32 +2162,51 @@ export class ObjectPropertyWidget extends UI.Widget.Widget {
1840
2162
  }
1841
2163
 
1842
2164
  set property(property: ObjectTreeNode) {
1843
- if (this.#property) {
1844
- this.#property.removeEventListener(ObjectTreeNodeBase.Events.VALUE_CHANGED, this.requestUpdate, this);
1845
- this.#property.removeEventListener(ObjectTreeNodeBase.Events.CHILDREN_CHANGED, this.requestUpdate, this);
1846
- this.#property.removeEventListener(ObjectTreeNodeBase.Events.FILTER_CHANGED, this.requestUpdate, this);
2165
+ if (property === this.#property) {
2166
+ return;
1847
2167
  }
1848
- this.#search?.removeEventListener(UI.TreeOutline.TreeSearch.Events.SEARCH_CHANGED, this.requestUpdate, this);
2168
+ this.#removePropertyListeners();
1849
2169
  this.#property = property;
1850
- this.#property.addEventListener(ObjectTreeNodeBase.Events.VALUE_CHANGED, this.requestUpdate, this);
1851
- this.#property.addEventListener(ObjectTreeNodeBase.Events.CHILDREN_CHANGED, this.requestUpdate, this);
1852
- this.#property.addEventListener(ObjectTreeNodeBase.Events.FILTER_CHANGED, this.requestUpdate, this);
1853
2170
  this.#search = property.search;
1854
- this.#search?.addEventListener(UI.TreeOutline.TreeSearch.Events.SEARCH_CHANGED, this.requestUpdate, this);
2171
+ this.#addPropertyListeners();
1855
2172
  this.requestUpdate();
1856
2173
  }
1857
2174
 
1858
- get expanded(): boolean {
1859
- return this.#expanded;
2175
+ #addPropertyListeners(): void {
2176
+ this.#property?.addEventListener(ObjectTreeNodeBase.Events.VALUE_CHANGED, this.requestUpdate, this);
2177
+ this.#property?.addEventListener(ObjectTreeNodeBase.Events.CHILDREN_CHANGED, this.requestUpdate, this);
2178
+ this.#property?.addEventListener(ObjectTreeNodeBase.Events.FILTER_CHANGED, this.requestUpdate, this);
2179
+ this.#search?.addEventListener(UI.TreeOutline.TreeSearch.Events.SEARCH_CHANGED, this.requestUpdate, this);
1860
2180
  }
1861
2181
 
1862
- set expanded(expanded: boolean) {
1863
- this.#expanded = expanded;
1864
- this.requestUpdate();
2182
+ #removePropertyListeners(): void {
2183
+ this.#property?.removeEventListener(ObjectTreeNodeBase.Events.VALUE_CHANGED, this.requestUpdate, this);
2184
+ this.#property?.removeEventListener(ObjectTreeNodeBase.Events.CHILDREN_CHANGED, this.requestUpdate, this);
2185
+ this.#property?.removeEventListener(ObjectTreeNodeBase.Events.FILTER_CHANGED, this.requestUpdate, this);
2186
+ this.#search?.removeEventListener(UI.TreeOutline.TreeSearch.Events.SEARCH_CHANGED, this.requestUpdate, this);
1865
2187
  }
1866
2188
 
1867
- get linkifier(): Components.Linkifier.Linkifier|undefined {
1868
- return this.#linkifier;
2189
+ override onDetach(): void {
2190
+ this.#removePropertyListeners();
2191
+ }
2192
+
2193
+ override wasShown(): void {
2194
+ super.wasShown();
2195
+ this.#removePropertyListeners();
2196
+ this.#addPropertyListeners();
2197
+ }
2198
+
2199
+ get expanded(): boolean {
2200
+ return this.#expanded;
2201
+ }
2202
+
2203
+ set expanded(expanded: boolean) {
2204
+ this.#expanded = expanded;
2205
+ this.requestUpdate();
2206
+ }
2207
+
2208
+ get linkifier(): Components.Linkifier.Linkifier|undefined {
2209
+ return this.#linkifier;
1869
2210
  }
1870
2211
 
1871
2212
  set linkifier(linkifier: Components.Linkifier.Linkifier|undefined) {
@@ -1934,493 +2275,6 @@ export class ObjectPropertyWidget extends UI.Widget.Widget {
1934
2275
  }
1935
2276
  }
1936
2277
 
1937
- class ObjectPropertyTreeElement extends UI.TreeOutline.TreeElement {
1938
- property: ObjectTreeNode;
1939
- override toggleOnClick: boolean;
1940
- private linkifier: Components.Linkifier.Linkifier|undefined;
1941
- private readonly maxNumPropertiesToShow: number;
1942
- readonly #widget: ObjectPropertyWidget;
1943
- constructor(property: ObjectTreeNode, linkifier?: Components.Linkifier.Linkifier) {
1944
- // Pass an empty title, the title gets made later in onattach.
1945
- super();
1946
-
1947
- this.#widget = new ObjectPropertyWidget();
1948
- this.#widget.markAsRoot();
1949
- this.property = property;
1950
- this.hidden = property.isFiltered;
1951
- this.property.addEventListener(ObjectTreeNodeBase.Events.VALUE_CHANGED, this.#updateValue, this);
1952
- this.property.addEventListener(ObjectTreeNodeBase.Events.CHILDREN_CHANGED, this.#updateChildren, this);
1953
- this.property.addEventListener(ObjectTreeNodeBase.Events.FILTER_CHANGED, this.#updateFilter, this);
1954
- this.property.addEventListener(ObjectTreeNodeBase.Events.EXPANDED_CHANGED, this.#onExpandedChanged, this);
1955
- this.toggleOnClick = true;
1956
- this.linkifier = linkifier;
1957
- this.maxNumPropertiesToShow = InitialVisibleChildrenLimit;
1958
- this.listItemElement.addEventListener('contextmenu', this.contextMenuFired.bind(this), false);
1959
- this.listItemElement.dataset.objectPropertyNameForTest = property.name;
1960
- this.updateExpandable();
1961
- this.setExpandRecursively(property.name !== '[[Prototype]]');
1962
- if (property.expanded) {
1963
- this.expand();
1964
- }
1965
- }
1966
-
1967
- static async populate(
1968
- treeElement: UI.TreeOutline.TreeElement,
1969
- value: ObjectTreeNodeBase,
1970
- skipProto: boolean,
1971
- skipGettersAndSetters: boolean,
1972
- linkifier?: Components.Linkifier.Linkifier,
1973
- emptyPlaceholder?: string|null,
1974
- ): Promise<void> {
1975
- await populateChildrenIfNeeded(value);
1976
- ObjectPropertyTreeElement.populateImpl(treeElement, value, skipProto, skipGettersAndSetters, linkifier,
1977
- emptyPlaceholder);
1978
- }
1979
-
1980
- static populateImpl(
1981
- treeElement: UI.TreeOutline.TreeElement,
1982
- value: ObjectTreeNodeBase,
1983
- skipProto: boolean,
1984
- skipGettersAndSetters: boolean,
1985
- linkifier?: Components.Linkifier.Linkifier,
1986
- emptyPlaceholder?: string|null,
1987
- ): void {
1988
- for (const childNode of ObjectPropertyTreeElement.createNodes(
1989
- value, skipProto, skipGettersAndSetters, linkifier, emptyPlaceholder,
1990
- property => treeElement instanceof ObjectPropertyTreeElement &&
1991
- !isDisplayableProperty(property, treeElement.property?.property))) {
1992
- treeElement.appendChild(childNode);
1993
- }
1994
- }
1995
-
1996
- static *
1997
- createNodes(
1998
- value: ObjectTreeNodeBase,
1999
- skipProto: boolean,
2000
- skipGettersAndSetters: boolean,
2001
- linkifier?: Components.Linkifier.Linkifier,
2002
- emptyPlaceholder?: string|null,
2003
- isNotDisplayablePropertyCallback?: (property: SDK.RemoteObject.RemoteObjectProperty) => boolean,
2004
- ): Generator<UI.TreeOutline.TreeElement> {
2005
- const properties = value.children;
2006
- if (!properties) {
2007
- return;
2008
- }
2009
- if (properties.arrayRanges) {
2010
- yield* ArrayGroupingTreeElement.createNodes(properties, linkifier, isNotDisplayablePropertyCallback);
2011
- } else {
2012
- yield*
2013
- ObjectPropertyTreeElement.createPropertyNodes(properties, skipProto, skipGettersAndSetters, linkifier,
2014
- emptyPlaceholder, isNotDisplayablePropertyCallback);
2015
- }
2016
- }
2017
-
2018
- static *
2019
- createPropertyNodes({properties, internalProperties, accessors, arrayRanges}: NodeChildren, skipProto: boolean,
2020
- skipGettersAndSetters: boolean, linkifier?: Components.Linkifier.Linkifier,
2021
- emptyPlaceholder?: string|null,
2022
- isNotDisplayablePropertyCallback?:
2023
- (property: SDK.RemoteObject.RemoteObjectProperty) => boolean):
2024
- Generator<UI.TreeOutline.TreeElement> {
2025
- let empty = true;
2026
- // Arrays with large numbers of elements are paginated into arrayRanges.
2027
- // If we have array ranges, the object is not empty.
2028
- if (arrayRanges && arrayRanges.length > 0) {
2029
- empty = false;
2030
- }
2031
- const sortPropertiesAlphabetically = properties?.[0]?.sortPropertiesAlphabetically ?? true;
2032
- properties?.sort((a, b) => compareProperties(a, b, sortPropertiesAlphabetically));
2033
-
2034
- const entriesProperty = internalProperties?.find(({property}) => property.name === '[[Entries]]');
2035
- if (entriesProperty) {
2036
- const treeElement = new ObjectPropertyTreeElement(entriesProperty, linkifier);
2037
- treeElement.setExpandable(true);
2038
- treeElement.expand();
2039
- empty = false;
2040
- yield treeElement;
2041
- }
2042
-
2043
- for (const property of properties ?? []) {
2044
- if (isNotDisplayablePropertyCallback?.(property.property)) {
2045
- continue;
2046
- }
2047
-
2048
- const canShowProperty = property.property.getter || !property.property.isAccessorProperty();
2049
- if (canShowProperty) {
2050
- const element = new ObjectPropertyTreeElement(property, linkifier);
2051
- if (property.property.name === 'memories' && property.object?.className === 'Memories') {
2052
- element.updateExpandable();
2053
- if (element.isExpandable()) {
2054
- element.expand();
2055
- }
2056
- }
2057
- empty = false;
2058
- yield element;
2059
- }
2060
- }
2061
-
2062
- for (const accessor of accessors ?? []) {
2063
- yield new ObjectPropertyTreeElement(accessor, linkifier);
2064
- }
2065
-
2066
- for (const property of internalProperties ?? []) {
2067
- const treeElement = new ObjectPropertyTreeElement(property, linkifier);
2068
- if (property.property.name === '[[Entries]]') {
2069
- continue;
2070
- }
2071
- if (property.property.name === '[[Prototype]]' && skipProto) {
2072
- continue;
2073
- }
2074
- empty = false;
2075
- yield treeElement;
2076
- }
2077
-
2078
- if (empty) {
2079
- const title = document.createElement('div');
2080
- title.classList.add('gray-info-message');
2081
- title.textContent = emptyPlaceholder || i18nString(UIStrings.noProperties);
2082
- const infoElement = new UI.TreeOutline.TreeElement(title);
2083
- yield infoElement;
2084
- }
2085
- }
2086
-
2087
- static populateWithProperties(treeNode: UI.TreeOutline.TreeElement, children: NodeChildren, skipProto: boolean,
2088
- skipGettersAndSetters: boolean, linkifier?: Components.Linkifier.Linkifier,
2089
- emptyPlaceholder?: string|null): void {
2090
- for (const childNode of this.createPropertyNodes(
2091
- children, skipProto, skipGettersAndSetters, linkifier, emptyPlaceholder,
2092
- property => treeNode instanceof ObjectPropertyTreeElement &&
2093
- !isDisplayableProperty(property, treeNode.property?.property))) {
2094
- treeNode.appendChild(childNode);
2095
- }
2096
- }
2097
-
2098
- // This is called by layout tests
2099
- startEditing(): void {
2100
- this.#widget.startEditing();
2101
- }
2102
-
2103
- // This is called by layout tests
2104
- get editing(): boolean {
2105
- return this.#widget.editing;
2106
- }
2107
-
2108
- get editable(): boolean {
2109
- return this.#widget.editable;
2110
- }
2111
-
2112
- set editable(val: boolean) {
2113
- this.#widget.editable = val;
2114
- }
2115
-
2116
- // This is called by layout tests
2117
- async applyExpression(expression: string): Promise<void> {
2118
- await this.property.setValue(expression);
2119
- }
2120
-
2121
- private showAllPropertiesElementSelected(element: UI.TreeOutline.TreeElement): boolean {
2122
- this.removeChild(element);
2123
- this.children().forEach(x => {
2124
- x.hidden = false;
2125
- });
2126
- return false;
2127
- }
2128
-
2129
- private createShowAllPropertiesButton(): void {
2130
- const element = document.createElement('div');
2131
- element.classList.add('object-value-calculate-value-button');
2132
- element.textContent = i18nString(UIStrings.dots);
2133
- UI.Tooltip.Tooltip.install(element, i18nString(UIStrings.showAllD, {PH1: this.childCount()}));
2134
- const children = this.children();
2135
- for (let i = this.maxNumPropertiesToShow; i < this.childCount(); ++i) {
2136
- children[i].hidden = true;
2137
- }
2138
- const showAllPropertiesButton = new UI.TreeOutline.TreeElement(element);
2139
- showAllPropertiesButton.onselect = this.showAllPropertiesElementSelected.bind(this, showAllPropertiesButton);
2140
- this.appendChild(showAllPropertiesButton);
2141
- }
2142
-
2143
- override async onpopulate(): Promise<void> {
2144
- this.removeChildren();
2145
-
2146
- if (this.property.object) {
2147
- await ObjectPropertyTreeElement.populate(this, this.property, false, false, this.linkifier);
2148
- if (this.childCount() > this.maxNumPropertiesToShow) {
2149
- this.createShowAllPropertiesButton();
2150
- }
2151
- }
2152
- }
2153
-
2154
- override onattach(): void {
2155
- this.updateExpandable();
2156
- this.#widget.show(this.listItemElement);
2157
- this.#widget.property = this.property;
2158
- this.#widget.linkifier = this.linkifier;
2159
- this.#widget.editable = !this.property.readOnly;
2160
- }
2161
-
2162
- override onexpand(): void {
2163
- this.property.expanded = true;
2164
- this.#widget.expanded = true;
2165
- }
2166
-
2167
- override oncollapse(): void {
2168
- this.property.expanded = false;
2169
- this.#widget.expanded = false;
2170
- }
2171
-
2172
- #updateValue(): void {
2173
- this.updateExpandable();
2174
- }
2175
-
2176
- #updateChildren(): void {
2177
- this.removeChildren();
2178
- void this.onpopulate();
2179
- }
2180
-
2181
- #updateFilter(): void {
2182
- this.hidden = this.property.isFiltered;
2183
- }
2184
-
2185
- #onExpandedChanged(event: Common.EventTarget.EventTargetEvent<boolean>): void {
2186
- const expanded = event.data;
2187
- if (expanded) {
2188
- this.expand();
2189
- } else {
2190
- this.collapse();
2191
- }
2192
- }
2193
-
2194
- private contextMenuFired(event: Event): void {
2195
- const contextMenu = new UI.ContextMenu.ContextMenu(event);
2196
- populateObjectTreeContextMenu(contextMenu, this.property, {
2197
- expandRecursively: () => this.expandRecursively(EXPANDABLE_MAX_DEPTH),
2198
- collapseChildren: () => this.collapseChildren(),
2199
- sortPropertiesAlphabetically: node => {
2200
- node.sortPropertiesAlphabetically = !node.sortPropertiesAlphabetically;
2201
- },
2202
- onShowAllToggled: node => {
2203
- node.includeNullOrUndefinedValues = !node.includeNullOrUndefinedValues;
2204
- },
2205
- });
2206
-
2207
- void contextMenu.show();
2208
- }
2209
-
2210
- private updateExpandable(): void {
2211
- if (this.property.object) {
2212
- this.setExpandable(!this.property.object.customPreview() && this.property.object.hasChildren &&
2213
- !this.property.property.wasThrown);
2214
- } else {
2215
- this.setExpandable(false);
2216
- }
2217
- }
2218
- }
2219
-
2220
- async function arrayRangeGroups(object: SDK.RemoteObject.RemoteObject, fromIndex: number,
2221
- toIndex: number): Promise<{ranges: number[][]}|null|undefined> {
2222
- return await object.callFunctionJSON(packArrayRanges, [
2223
- {value: fromIndex},
2224
- {value: toIndex},
2225
- {value: ARRAY_BUCKET_THRESHOLD},
2226
- {value: ARRAY_SPARSE_ITERATION_THRESHOLD},
2227
- ]);
2228
-
2229
- /**
2230
- * This function is called on the RemoteObject.
2231
- * Note: must declare params as optional.
2232
- */
2233
- function packArrayRanges(this: Object, fromIndex?: number, toIndex?: number, bucketThreshold?: number,
2234
- sparseIterationThreshold?: number): {
2235
- ranges: number[][],
2236
- }|undefined {
2237
- if (fromIndex === undefined || toIndex === undefined || sparseIterationThreshold === undefined ||
2238
- bucketThreshold === undefined) {
2239
- return;
2240
- }
2241
- let ownPropertyNames: string[]|null = null;
2242
- const consecutiveRange = (toIndex - fromIndex >= sparseIterationThreshold) && ArrayBuffer.isView(this);
2243
-
2244
- function* arrayIndexes(object: Object): Generator<number, void, unknown> {
2245
- if (fromIndex === undefined || toIndex === undefined || sparseIterationThreshold === undefined) {
2246
- return;
2247
- }
2248
-
2249
- if (toIndex - fromIndex < sparseIterationThreshold) {
2250
- for (let i = fromIndex; i <= toIndex; ++i) {
2251
- if (i in object) {
2252
- yield i;
2253
- }
2254
- }
2255
- } else {
2256
- ownPropertyNames = ownPropertyNames || Object.getOwnPropertyNames(object);
2257
- for (let i = 0; i < ownPropertyNames.length; ++i) {
2258
- const name = ownPropertyNames[i];
2259
-
2260
- const index = Number(name) >>> 0;
2261
- if ((String(index)) === name && fromIndex <= index && index <= toIndex) {
2262
- yield index;
2263
- }
2264
- }
2265
- }
2266
- }
2267
-
2268
- let count = 0;
2269
- if (consecutiveRange) {
2270
- count = toIndex - fromIndex + 1;
2271
- } else {
2272
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
2273
- for (const ignored of arrayIndexes(this)) {
2274
- ++count;
2275
- }
2276
- }
2277
-
2278
- let bucketSize: number = count;
2279
- if (count <= bucketThreshold) {
2280
- bucketSize = count;
2281
- } else {
2282
- bucketSize = Math.pow(bucketThreshold, Math.ceil(Math.log(count) / Math.log(bucketThreshold)) - 1);
2283
- }
2284
-
2285
- const ranges = [];
2286
- if (consecutiveRange) {
2287
- for (let i = fromIndex; i <= toIndex; i += bucketSize) {
2288
- const groupStart = i;
2289
- let groupEnd: number = groupStart + bucketSize - 1;
2290
- if (groupEnd > toIndex) {
2291
- groupEnd = toIndex;
2292
- }
2293
- ranges.push([groupStart, groupEnd, groupEnd - groupStart + 1]);
2294
- }
2295
- } else {
2296
- count = 0;
2297
- let groupStart = -1;
2298
- let groupEnd = 0;
2299
- for (const i of arrayIndexes(this)) {
2300
- if (groupStart === -1) {
2301
- groupStart = i;
2302
- }
2303
- groupEnd = i;
2304
- if (++count === bucketSize) {
2305
- ranges.push([groupStart, groupEnd, count]);
2306
- count = 0;
2307
- groupStart = -1;
2308
- }
2309
- }
2310
- if (count > 0) {
2311
- ranges.push([groupStart, groupEnd, count]);
2312
- }
2313
- }
2314
-
2315
- return {ranges};
2316
- }
2317
- }
2318
-
2319
- /**
2320
- * This function is called on the RemoteObject.
2321
- */
2322
- function buildArrayFragment(
2323
- this: Record<number, Object>,
2324
- fromIndex?: number,
2325
- toIndex?: number,
2326
- sparseIterationThreshold?: number,
2327
- ): unknown {
2328
- const result = Object.create(null);
2329
-
2330
- if (fromIndex === undefined || toIndex === undefined || sparseIterationThreshold === undefined) {
2331
- return;
2332
- }
2333
-
2334
- if (toIndex - fromIndex < sparseIterationThreshold) {
2335
- for (let i = fromIndex; i <= toIndex; ++i) {
2336
- if (i in this) {
2337
- result[i] = this[i];
2338
- }
2339
- }
2340
- } else {
2341
- const ownPropertyNames = Object.getOwnPropertyNames(this);
2342
- for (let i = 0; i < ownPropertyNames.length; ++i) {
2343
- const name = ownPropertyNames[i];
2344
- const index = Number(name) >>> 0;
2345
- if (String(index) === name && fromIndex <= index && index <= toIndex) {
2346
- result[index] = this[index];
2347
- }
2348
- }
2349
- }
2350
- return result;
2351
- }
2352
-
2353
- class ArrayGroupingTreeElement extends UI.TreeOutline.TreeElement {
2354
- override toggleOnClick: boolean;
2355
- private readonly linkifier: Components.Linkifier.Linkifier|undefined;
2356
- readonly #child: ArrayGroupTreeNode;
2357
- constructor(child: ArrayGroupTreeNode, linkifier?: Components.Linkifier.Linkifier) {
2358
- super(Platform.StringUtilities.sprintf('[%d … %d]', child.range.fromIndex, child.range.toIndex), true);
2359
- this.#child = child;
2360
- this.#child.addEventListener(ObjectTreeNodeBase.Events.CHILDREN_CHANGED, this.onpopulate, this);
2361
- this.#child.addEventListener(ObjectTreeNodeBase.Events.EXPANDED_CHANGED, this.#onExpandedChanged, this);
2362
- this.toggleOnClick = true;
2363
- this.linkifier = linkifier;
2364
- if (child.expanded) {
2365
- this.expand();
2366
- }
2367
- }
2368
-
2369
- #onExpandedChanged(event: Common.EventTarget.EventTargetEvent<boolean>): void {
2370
- const expanded = event.data;
2371
- if (expanded) {
2372
- this.expand();
2373
- } else {
2374
- this.collapse();
2375
- }
2376
- }
2377
-
2378
- static *
2379
- createNodes(children: NodeChildren, linkifier?: Components.Linkifier.Linkifier,
2380
- isNotDisplayablePropertyCallback?: (property: SDK.RemoteObject.RemoteObjectProperty) => boolean):
2381
- Generator<UI.TreeOutline.TreeElement> {
2382
- if (!children.arrayRanges) {
2383
- return;
2384
- }
2385
- if (children.arrayRanges.length === 1) {
2386
- yield*
2387
- ObjectPropertyTreeElement.createNodes(children.arrayRanges[0], false, false, linkifier, null,
2388
- isNotDisplayablePropertyCallback);
2389
- } else {
2390
- for (const child of children.arrayRanges) {
2391
- if (child.singular) {
2392
- yield*
2393
- ObjectPropertyTreeElement.createNodes(child, false, false, linkifier, null,
2394
- isNotDisplayablePropertyCallback);
2395
- } else {
2396
- yield new ArrayGroupingTreeElement(child, linkifier);
2397
- }
2398
- }
2399
- }
2400
-
2401
- yield*
2402
- ObjectPropertyTreeElement.createPropertyNodes(children, false, false, linkifier, null,
2403
- isNotDisplayablePropertyCallback);
2404
- }
2405
-
2406
- override onexpand(): void {
2407
- this.#child.expanded = true;
2408
- }
2409
-
2410
- override oncollapse(): void {
2411
- this.#child.expanded = false;
2412
- }
2413
-
2414
- override async onpopulate(): Promise<void> {
2415
- this.removeChildren();
2416
- await ObjectPropertyTreeElement.populate(this, this.#child, false, false, this.linkifier);
2417
- }
2418
-
2419
- override onattach(): void {
2420
- this.listItemElement.classList.add('object-properties-section-name');
2421
- }
2422
- }
2423
-
2424
2278
  interface ExpandableTextViewInput {
2425
2279
  copyText: () => void;
2426
2280
  expandText: () => void;