chrome-devtools-frontend 1.0.1702515 → 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.
@@ -383,6 +383,139 @@ const ARRAY_LOAD_THRESHOLD = 100;
383
383
  const ARRAY_BUCKET_THRESHOLD = 100;
384
384
  const ARRAY_SPARSE_ITERATION_THRESHOLD = 250000;
385
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
+
386
519
  export abstract class ObjectTreeNodeBase extends Common.ObjectWrapper.ObjectWrapper<ObjectTreeNodeBase.EventTypes> {
387
520
  #children?: NodeChildren;
388
521
  protected readonly options: ObjectTreeOptions;
@@ -1249,11 +1382,6 @@ export interface ContextMenuHandlers {
1249
1382
  onShowAllToggled: (node: ObjectTreeNodeBase) => void;
1250
1383
  }
1251
1384
 
1252
- interface StateHandlers extends ContextMenuHandlers {
1253
- onShowAllProperties: (node: ObjectTreeNode) => void;
1254
- onExpand: (node: ObjectTreeNodeBase, expanded: boolean) => void;
1255
- }
1256
-
1257
1385
  export function populateObjectTreeContextMenu(contextMenu: UI.ContextMenu.ContextMenu,
1258
1386
  objectOrProperty: ObjectTree|ObjectTreeNode,
1259
1387
  handlers: ContextMenuHandlers): void {
@@ -1311,7 +1439,9 @@ export function populateObjectTreeContextMenu(contextMenu: UI.ContextMenu.Contex
1311
1439
  {checked: objectOrProperty.includeNullOrUndefinedValues, jslogContext: 'show-all'});
1312
1440
  }
1313
1441
 
1314
- interface ObjectTreeViewInput extends StateHandlers {
1442
+ interface ObjectTreeViewInput extends ContextMenuHandlers {
1443
+ onShowAllProperties: (node: ObjectTreeNode) => void;
1444
+ onExpand: (node: ObjectTreeNodeBase, expanded: boolean) => void;
1315
1445
  renderAsSubtree: boolean;
1316
1446
  objectTree?: ObjectTree;
1317
1447
  linkifier?: Components.Linkifier.Linkifier;
@@ -1327,67 +1457,225 @@ const OBJECT_TREE_DEFAULT_VIEW: ObjectTreeView = (input, output, target) => {
1327
1457
  return;
1328
1458
  }
1329
1459
 
1330
- const nodes = Array.from(ObjectPropertyTreeElement.createNodes(
1331
- objectTree, input, input.skipProto, input.skipGettersAndSetters, input.linkifier, input.emptyPlaceholder));
1332
- nodes.forEach(UI.UIUtils.HTMLElementWithLightDOMTemplate.patchLitTemplate);
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();
1465
+ };
1333
1466
 
1334
- render(nodes, target, {
1335
- container: {classes: input.renderAsSubtree ? ['source-code', 'object-properties-section'] : []},
1336
- });
1337
- };
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);
1338
1474
 
1339
- export class ObjectTreeWidget extends UI.Widget.Widget {
1340
- #objectTree: ObjectTree|undefined = undefined;
1341
- #linkifier: Components.Linkifier.Linkifier|undefined = undefined;
1342
- #emptyPlaceholder?: LitTemplate;
1343
- #renderAsSubtree = false;
1344
- #skipProto = false;
1345
- #skipGettersAndSetters = false;
1346
- readonly #view: ObjectTreeView;
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
+ };
1347
1480
 
1348
- constructor(element?: HTMLElement, view: ObjectTreeView = OBJECT_TREE_DEFAULT_VIEW) {
1349
- super(element);
1350
- this.#view = view;
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
1351
1508
  }
1352
1509
 
1353
- async #populateChildrenIfNeeded(node: ObjectTreeNodeBase): Promise<void> {
1354
- const isFirstPopulation = !node.children;
1355
- const children = await node.populateChildrenIfNeeded();
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
+ };
1356
1515
 
1357
- if (isFirstPopulation) {
1358
- const entriesProperty = children.internalProperties?.find(({property}) => property.name === '[[Entries]]');
1359
- if (entriesProperty) {
1360
- entriesProperty.expanded = true;
1361
- }
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
+ }
1362
1530
 
1363
- for (const property of children.properties ?? []) {
1364
- const canShowProperty = property.property.getter || !property.property.isAccessorProperty();
1365
- if (canShowProperty) {
1366
- if (property.property.name === 'memories' && property.object?.className === 'Memories') {
1367
- property.expanded = true;
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);
1368
1552
  }
1369
1553
  }
1370
1554
  }
1371
- }
1372
1555
 
1373
- for (const child of node.treeNodeChildren()) {
1374
- child.removeEventListener(ObjectTreeNodeBase.Events.FILTER_CHANGED, this.requestUpdate, this);
1375
- child.removeEventListener(ObjectTreeNodeBase.Events.EXPANDED_CHANGED, this.requestUpdate, this);
1376
- child.removeEventListener(ObjectTreeNodeBase.Events.VALUE_CHANGED, this.requestUpdate, this);
1377
- child.removeEventListener(ObjectTreeNodeBase.Events.CHILDREN_CHANGED, this.requestUpdate, this);
1378
- child.addEventListener(ObjectTreeNodeBase.Events.FILTER_CHANGED, this.requestUpdate, this);
1379
- child.addEventListener(ObjectTreeNodeBase.Events.EXPANDED_CHANGED, this.requestUpdate, this);
1380
- child.addEventListener(ObjectTreeNodeBase.Events.VALUE_CHANGED, this.requestUpdate, this);
1381
- child.addEventListener(ObjectTreeNodeBase.Events.CHILDREN_CHANGED, this.requestUpdate, this);
1382
- if (child.expanded) {
1383
- await this.#populateChildrenIfNeeded(child);
1384
- }
1556
+ yield* createPropertyNodes(properties, false, false, undefined, isNotDisplayablePropertyCallback);
1557
+ } else {
1558
+ yield*
1559
+ createPropertyNodes(properties, skipProto, skipGettersAndSetters, emptyPlaceholder,
1560
+ isNotDisplayablePropertyCallback);
1385
1561
  }
1562
+ }
1386
1563
 
1387
- if (!children.arrayRanges) {
1388
- return;
1389
- }
1390
- if (children.arrayRanges.length === 1) {
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
+ }
1616
+ }
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
+ };
1626
+
1627
+ export class ObjectTreeWidget extends UI.Widget.Widget {
1628
+ #objectTree: ObjectTree|undefined = undefined;
1629
+ #linkifier: Components.Linkifier.Linkifier|undefined = undefined;
1630
+ #emptyPlaceholder?: LitTemplate;
1631
+ #renderAsSubtree = false;
1632
+ #skipProto = false;
1633
+ #skipGettersAndSetters = false;
1634
+ readonly #view: ObjectTreeView;
1635
+
1636
+ constructor(element?: HTMLElement, view: ObjectTreeView = OBJECT_TREE_DEFAULT_VIEW) {
1637
+ super(element);
1638
+ this.#view = view;
1639
+ }
1640
+
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) {
1391
1679
  await this.#populateChildrenIfNeeded(children.arrayRanges[0]);
1392
1680
  } else {
1393
1681
  await Promise.all(
@@ -1987,325 +2275,6 @@ export class ObjectPropertyWidget extends UI.Widget.Widget {
1987
2275
  }
1988
2276
  }
1989
2277
 
1990
- class ObjectPropertyTreeElement {
1991
- static #render(property: ObjectTreeNode, linkifier: Components.Linkifier.Linkifier|undefined,
1992
- handlers: StateHandlers): LitTemplate {
1993
- const onContextMenu = (e: Event): void => {
1994
- const contextMenu = new UI.ContextMenu.ContextMenu(e);
1995
- populateObjectTreeContextMenu(contextMenu, property, handlers);
1996
- void contextMenu.show();
1997
- };
1998
-
1999
- const onExpand = (e: UI.TreeOutline.TreeViewElement.ExpandEvent): void => {
2000
- handlers.onExpand(property, e.detail.expanded);
2001
- e.consume(true);
2002
- };
2003
- const childCount = (property.children?.properties?.length ?? 0) +
2004
- (property.children?.internalProperties?.length ?? 0) + (property.children?.accessors?.length ?? 0) +
2005
- (property.children?.arrayRanges?.length ?? 0);
2006
-
2007
- const showAllChildren = childCount <= InitialVisibleChildrenLimit || property.showAllChildren;
2008
- const children = (): Iterable<LitTemplate> => {
2009
- const nodes = ObjectPropertyTreeElement.createNodes(property, handlers, false, false, linkifier, undefined,
2010
- p => !isDisplayableProperty(p, property.property));
2011
- return showAllChildren ? nodes : nodes.take(InitialVisibleChildrenLimit);
2012
- };
2013
-
2014
- const expandable = property.object && !property.object.customPreview() && property.object.hasChildren &&
2015
- !property.property.wasThrown;
2016
-
2017
- // clang-format off
2018
- return html`<li
2019
- ?hidden=${property.isFiltered}
2020
- ?open=${property.expanded}
2021
- @contextmenu=${onContextMenu}
2022
- @expand=${onExpand}
2023
- data-object-property-name-for-test=${property.name}
2024
- toggle-on-click
2025
- role=treeitem>
2026
- ${widget(ObjectPropertyWidget, {property, linkifier, editable: !property.readOnly, expanded: property.expanded})}
2027
- ${expandable ? html`
2028
- <ul role=group>
2029
- ${children()}
2030
- ${showAllChildren ? nothing : html`
2031
- <li role=treeitem @select=${() => handlers.onShowAllProperties(property)}>
2032
- <div
2033
- class=object-value-calculate-value-button
2034
- title=${i18nString(UIStrings.showAllD, {PH1: childCount})}>
2035
- ${i18nString(UIStrings.dots)}
2036
- </div>
2037
- </li>`}
2038
- </ul>` : nothing}
2039
- </li>`;
2040
- // clang-format on
2041
- }
2042
-
2043
- static *
2044
- createNodes(
2045
- value: ObjectTreeNodeBase,
2046
- handlers: StateHandlers,
2047
- skipProto: boolean,
2048
- skipGettersAndSetters: boolean,
2049
- linkifier?: Components.Linkifier.Linkifier,
2050
- emptyPlaceholder?: LitTemplate,
2051
- isNotDisplayablePropertyCallback?: (property: SDK.RemoteObject.RemoteObjectProperty) => boolean,
2052
- ): Generator<LitTemplate> {
2053
- const properties = value.children;
2054
- if (!properties) {
2055
- return;
2056
- }
2057
- if (properties.arrayRanges) {
2058
- yield* ArrayGroupingTreeElement.createNodes(properties, handlers, linkifier, isNotDisplayablePropertyCallback);
2059
- } else {
2060
- yield*
2061
- ObjectPropertyTreeElement.createPropertyNodes(properties, handlers, skipProto, skipGettersAndSetters,
2062
- linkifier, emptyPlaceholder, isNotDisplayablePropertyCallback);
2063
- }
2064
- }
2065
-
2066
- static *
2067
- createPropertyNodes({properties, internalProperties, accessors, arrayRanges}: NodeChildren,
2068
- handlers: StateHandlers, skipProto: boolean, skipGettersAndSetters: boolean,
2069
- linkifier?: Components.Linkifier.Linkifier, emptyPlaceholder?: LitTemplate,
2070
- isNotDisplayablePropertyCallback?:
2071
- (property: SDK.RemoteObject.RemoteObjectProperty) => boolean): Generator<LitTemplate> {
2072
- let empty = true;
2073
- // Arrays with large numbers of elements are paginated into arrayRanges.
2074
- // If we have array ranges, the object is not empty.
2075
- if (arrayRanges && arrayRanges.length > 0) {
2076
- empty = false;
2077
- }
2078
- const sortPropertiesAlphabetically = properties?.[0]?.sortPropertiesAlphabetically ?? true;
2079
- properties?.sort((a, b) => compareProperties(a, b, sortPropertiesAlphabetically));
2080
-
2081
- const entriesProperty = internalProperties?.find(({property}) => property.name === '[[Entries]]');
2082
- if (entriesProperty) {
2083
- yield ObjectPropertyTreeElement.#render(entriesProperty, linkifier, handlers);
2084
- empty = false;
2085
- }
2086
-
2087
- for (const property of properties ?? []) {
2088
- if (isNotDisplayablePropertyCallback?.(property.property)) {
2089
- continue;
2090
- }
2091
-
2092
- const canShowProperty = property.property.getter || !property.property.isAccessorProperty();
2093
- if (canShowProperty) {
2094
- yield ObjectPropertyTreeElement.#render(property, linkifier, handlers);
2095
- empty = false;
2096
- }
2097
- }
2098
-
2099
- if (!skipGettersAndSetters) {
2100
- for (const accessor of accessors ?? []) {
2101
- yield ObjectPropertyTreeElement.#render(accessor, linkifier, handlers);
2102
- }
2103
- }
2104
-
2105
- for (const property of internalProperties ?? []) {
2106
- if (property.property.name === '[[Entries]]') {
2107
- continue;
2108
- }
2109
- if (property.property.name === '[[Prototype]]' && skipProto) {
2110
- continue;
2111
- }
2112
- yield ObjectPropertyTreeElement.#render(property, linkifier, handlers);
2113
- empty = false;
2114
- }
2115
-
2116
- if (empty) {
2117
- yield html`<li role=treeitem>
2118
- <div class=gray-info-message>${emptyPlaceholder || i18nString(UIStrings.noProperties)}</div>
2119
- </li>`;
2120
- }
2121
- }
2122
- }
2123
-
2124
- async function arrayRangeGroups(object: SDK.RemoteObject.RemoteObject, fromIndex: number,
2125
- toIndex: number): Promise<{ranges: number[][]}|null|undefined> {
2126
- return await object.callFunctionJSON(packArrayRanges, [
2127
- {value: fromIndex},
2128
- {value: toIndex},
2129
- {value: ARRAY_BUCKET_THRESHOLD},
2130
- {value: ARRAY_SPARSE_ITERATION_THRESHOLD},
2131
- ]);
2132
-
2133
- /**
2134
- * This function is called on the RemoteObject.
2135
- * Note: must declare params as optional.
2136
- */
2137
- function packArrayRanges(this: Object, fromIndex?: number, toIndex?: number, bucketThreshold?: number,
2138
- sparseIterationThreshold?: number): {
2139
- ranges: number[][],
2140
- }|undefined {
2141
- if (fromIndex === undefined || toIndex === undefined || sparseIterationThreshold === undefined ||
2142
- bucketThreshold === undefined) {
2143
- return;
2144
- }
2145
- let ownPropertyNames: string[]|null = null;
2146
- const consecutiveRange = (toIndex - fromIndex >= sparseIterationThreshold) && ArrayBuffer.isView(this);
2147
-
2148
- function* arrayIndexes(object: Object): Generator<number, void, unknown> {
2149
- if (fromIndex === undefined || toIndex === undefined || sparseIterationThreshold === undefined) {
2150
- return;
2151
- }
2152
-
2153
- if (toIndex - fromIndex < sparseIterationThreshold) {
2154
- for (let i = fromIndex; i <= toIndex; ++i) {
2155
- if (i in object) {
2156
- yield i;
2157
- }
2158
- }
2159
- } else {
2160
- ownPropertyNames = ownPropertyNames || Object.getOwnPropertyNames(object);
2161
- for (let i = 0; i < ownPropertyNames.length; ++i) {
2162
- const name = ownPropertyNames[i];
2163
-
2164
- const index = Number(name) >>> 0;
2165
- if ((String(index)) === name && fromIndex <= index && index <= toIndex) {
2166
- yield index;
2167
- }
2168
- }
2169
- }
2170
- }
2171
-
2172
- let count = 0;
2173
- if (consecutiveRange) {
2174
- count = toIndex - fromIndex + 1;
2175
- } else {
2176
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
2177
- for (const ignored of arrayIndexes(this)) {
2178
- ++count;
2179
- }
2180
- }
2181
-
2182
- let bucketSize: number = count;
2183
- if (count <= bucketThreshold) {
2184
- bucketSize = count;
2185
- } else {
2186
- bucketSize = Math.pow(bucketThreshold, Math.ceil(Math.log(count) / Math.log(bucketThreshold)) - 1);
2187
- }
2188
-
2189
- const ranges = [];
2190
- if (consecutiveRange) {
2191
- for (let i = fromIndex; i <= toIndex; i += bucketSize) {
2192
- const groupStart = i;
2193
- let groupEnd: number = groupStart + bucketSize - 1;
2194
- if (groupEnd > toIndex) {
2195
- groupEnd = toIndex;
2196
- }
2197
- ranges.push([groupStart, groupEnd, groupEnd - groupStart + 1]);
2198
- }
2199
- } else {
2200
- count = 0;
2201
- let groupStart = -1;
2202
- let groupEnd = 0;
2203
- for (const i of arrayIndexes(this)) {
2204
- if (groupStart === -1) {
2205
- groupStart = i;
2206
- }
2207
- groupEnd = i;
2208
- if (++count === bucketSize) {
2209
- ranges.push([groupStart, groupEnd, count]);
2210
- count = 0;
2211
- groupStart = -1;
2212
- }
2213
- }
2214
- if (count > 0) {
2215
- ranges.push([groupStart, groupEnd, count]);
2216
- }
2217
- }
2218
-
2219
- return {ranges};
2220
- }
2221
- }
2222
-
2223
- /**
2224
- * This function is called on the RemoteObject.
2225
- */
2226
- function buildArrayFragment(
2227
- this: Record<number, Object>,
2228
- fromIndex?: number,
2229
- toIndex?: number,
2230
- sparseIterationThreshold?: number,
2231
- ): unknown {
2232
- const result = Object.create(null);
2233
-
2234
- if (fromIndex === undefined || toIndex === undefined || sparseIterationThreshold === undefined) {
2235
- return;
2236
- }
2237
-
2238
- if (toIndex - fromIndex < sparseIterationThreshold) {
2239
- for (let i = fromIndex; i <= toIndex; ++i) {
2240
- if (i in this) {
2241
- result[i] = this[i];
2242
- }
2243
- }
2244
- } else {
2245
- const ownPropertyNames = Object.getOwnPropertyNames(this);
2246
- for (let i = 0; i < ownPropertyNames.length; ++i) {
2247
- const name = ownPropertyNames[i];
2248
- const index = Number(name) >>> 0;
2249
- if (String(index) === name && fromIndex <= index && index <= toIndex) {
2250
- result[index] = this[index];
2251
- }
2252
- }
2253
- }
2254
- return result;
2255
- }
2256
-
2257
- class ArrayGroupingTreeElement {
2258
- static #render(child: ArrayGroupTreeNode, linkifier: Components.Linkifier.Linkifier|undefined,
2259
- handlers: StateHandlers): LitTemplate {
2260
- const onExpand = (e: UI.TreeOutline.TreeViewElement.ExpandEvent): void => {
2261
- handlers.onExpand(child, e.detail.expanded);
2262
- e.consume(true);
2263
- };
2264
-
2265
- // clang-format off
2266
- return html`<li
2267
- class=object-properties-section-name
2268
- role=treeitem
2269
- toggle-on-click
2270
- ?open=${child.expanded}
2271
- @expand=${onExpand}>
2272
- ${Platform.StringUtilities.sprintf('[%d … %d]', child.range.fromIndex, child.range.toIndex)}
2273
- <ul role=group>
2274
- ${ifExpanded(() => ObjectPropertyTreeElement.createNodes(child, handlers, false, false, linkifier))}
2275
- </ul>
2276
- </li>`;
2277
- // clang-format on
2278
- }
2279
-
2280
- static *
2281
- createNodes(children: NodeChildren, handlers: StateHandlers, linkifier?: Components.Linkifier.Linkifier,
2282
- isNotDisplayablePropertyCallback?: (property: SDK.RemoteObject.RemoteObjectProperty) => boolean):
2283
- Generator<LitTemplate> {
2284
- if (!children.arrayRanges) {
2285
- return;
2286
- }
2287
- if (children.arrayRanges.length === 1) {
2288
- yield*
2289
- ObjectPropertyTreeElement.createNodes(children.arrayRanges[0], handlers, false, false, linkifier, undefined,
2290
- isNotDisplayablePropertyCallback);
2291
- } else {
2292
- for (const child of children.arrayRanges) {
2293
- if (child.singular) {
2294
- yield*
2295
- ObjectPropertyTreeElement.createNodes(child, handlers, false, false, linkifier, undefined,
2296
- isNotDisplayablePropertyCallback);
2297
- } else {
2298
- yield ArrayGroupingTreeElement.#render(child, linkifier, handlers);
2299
- }
2300
- }
2301
- }
2302
-
2303
- yield*
2304
- ObjectPropertyTreeElement.createPropertyNodes(children, handlers, false, false, linkifier, undefined,
2305
- isNotDisplayablePropertyCallback);
2306
- }
2307
- }
2308
-
2309
2278
  interface ExpandableTextViewInput {
2310
2279
  copyText: () => void;
2311
2280
  expandText: () => void;