• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (C) 2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16import { BaseElement, element } from '../../../../../base-ui/BaseElement';
17import { LitTable } from '../../../../../base-ui/table/lit-table';
18import '../../../../../base-ui/table/lit-table';
19import { type ConstructorItem, FileInfo } from '../../../../../js-heap/model/UiStruct';
20import { HeapDataInterface } from '../../../../../js-heap/HeapDataInterface';
21import '../../../../../base-ui/table/lit-table-column';
22import { TabPaneJsMemoryFilter } from '../TabPaneJsMemoryFilter';
23import '../TabPaneJsMemoryFilter';
24import { LitProgressBar } from '../../../../../base-ui/progress-bar/LitProgressBar';
25import '../../../../../base-ui/progress-bar/LitProgressBar';
26import '../../../../../base-ui/slicer/lit-slicer';
27import { HeapSnapshotStruct } from '../../../../database/ui-worker/ProcedureWorkerHeapSnapshot';
28import { HeapTraceFunctionInfo } from '../../../../../js-heap/model/DatabaseStruct';
29import { TabPaneSummaryHtml } from './TabPaneSummary.html';
30
31@element('tabpane-summary')
32export class TabPaneSummary extends BaseElement {
33  private tblSummary: LitTable | null | undefined;
34  private tbs: LitTable | null | undefined;
35  private stackTable: LitTable | null | undefined;
36  private summary: Array<ConstructorItem> = [];
37  private retainsData: Array<ConstructorItem> = [];
38  private stackData: Array<HeapTraceFunctionInfo> = [];
39  private stackText: HTMLElement | undefined;
40  private fileSize: number = 0;
41  private tabFilter: TabPaneJsMemoryFilter | undefined | null;
42  private progressEL: LitProgressBar | null | undefined;
43  private summaryFilter: Array<ConstructorItem> = [];
44  private summaryData: Array<ConstructorItem> = [];
45  private search: HTMLInputElement | null | undefined;
46  private tbsTable: HTMLDivElement | null | undefined;
47  private tblTable: HTMLDivElement | null | undefined;
48  private rightTheadTable: HTMLDivElement | null | undefined;
49  private leftTheadTable: HTMLDivElement | null | undefined;
50  private leftArray: ConstructorItem[] = [];
51  private rightArray: ConstructorItem[] = [];
52  private stack: HTMLLIElement | null | undefined;
53  private retainers: HTMLLIElement | null | undefined;
54  private file: FileInfo | undefined | null;
55  private leftTable: HTMLDivElement | null | undefined;
56
57  initElements(): void {
58    this.tblSummary = this.shadowRoot?.querySelector<LitTable>('#summary_left');
59    this.tbs = this.shadowRoot?.querySelector<LitTable>('#summary_right');
60    this.stackTable = this.shadowRoot?.querySelector<LitTable>('#stackTable');
61    this.stackText = this.shadowRoot?.querySelector('.stackText') as HTMLElement;
62    this.tabFilter = this.shadowRoot?.querySelector('#filter') as TabPaneJsMemoryFilter;
63    this.progressEL = this.shadowRoot?.querySelector('.summary_progress') as LitProgressBar;
64    this.search = this.tabFilter?.shadowRoot?.querySelector('#js-memory-filter-input') as HTMLInputElement;
65    this.stack = this.shadowRoot?.querySelector('#stack') as HTMLLIElement;
66    this.retainers = this.shadowRoot?.querySelector('#retainers') as HTMLLIElement;
67    this.tblTable = this.tblSummary!.shadowRoot?.querySelector('.table') as HTMLDivElement;
68    this.rightTheadTable = this.tbs!.shadowRoot?.querySelector('.thead') as HTMLDivElement;
69    this.leftTheadTable = this.tblSummary!.shadowRoot?.querySelector('.thead') as HTMLDivElement;
70    this.tbsTable = this.tbs!.shadowRoot?.querySelector('.table') as HTMLDivElement;
71    this.leftTable = this.shadowRoot?.querySelector('#summary_left_table') as HTMLDivElement;
72    this.classFilter();
73  }
74
75  setSnapshotData(
76    data: HeapSnapshotStruct,
77    dataListCache: Array<HeapSnapshotStruct>,
78    scrollCallback: ((d: HeapSnapshotStruct, ds: Array<HeapSnapshotStruct>) => void) | undefined
79  ): void {
80    if (scrollCallback) {
81      scrollCallback(data, dataListCache);
82    }
83    this.summary = [];
84    this.initSummaryData(data);
85  }
86
87  initSummaryData(file: FileInfo | HeapSnapshotStruct, minNodeId?: number, maxNodeId?: number): void {
88    this.file = file as FileInfo;
89    this.clear();
90    this.summary = [];
91    this.progressEL!.loading = true;
92    this.summary = HeapDataInterface.getInstance().getClassesListForSummary(file.id, minNodeId, maxNodeId);
93    this.fileSize = file.size;
94    this.summary.forEach((summaryEl: any) => {
95      if (summaryEl.childCount > 1) {
96        let count = `${summaryEl.nodeName  } ×${summaryEl.childCount}`;
97        summaryEl.objectName = count;
98        summaryEl.count = ` ×${summaryEl.childCount}`;
99      } else {
100        summaryEl.objectName = summaryEl.nodeName;
101      }
102      let shallow = `${Math.round((summaryEl.shallowSize / file.size) * 100)  }%`;
103      let retained = `${Math.round((summaryEl.retainedSize / file.size) * 100)  }%`;
104      summaryEl.shallowPercent = shallow;
105      summaryEl.retainedPercent = retained;
106      if (summaryEl.distance >= 100000000 || summaryEl.distance === -5) {
107        summaryEl.distance = '-';
108      }
109    });
110    if (this.summary.length > 0) {
111      this.summaryData = this.summary;
112      this.tblSummary!.snapshotDataSource = this.summary;
113      this.progressEL!.loading = false;
114    } else {
115      this.tblSummary!.snapshotDataSource = [];
116      this.progressEL!.loading = false;
117    }
118    new ResizeObserver(() => {
119      if (this.parentElement?.clientHeight !== 0) {
120        this.tblSummary!.style.height = '100%';
121        this.tblSummary!.reMeauseHeight();
122      }
123    }).observe(this.parentElement!);
124    if (this.file!.name.includes('Timeline')) {
125      this.retainers!.classList.add('active');
126      this.stack!.style.display = 'flex';
127      this.retainers!.style.pointerEvents = 'auto';
128    } else {
129      this.stack!.style.display = 'none';
130      this.retainers!.classList.remove('active');
131      this.retainers!.style.pointerEvents = 'none';
132    }
133    this.clickToggleTable();
134  }
135
136  private retainsTableByDistance(currentLeftItem: ConstructorItem, sort: number): void {
137    const getList = function (list: Array<ConstructorItem>): void {
138      list.sort((leftA, rightB) => {
139        return sort === 1 ? leftA.distance - rightB.distance : rightB.distance - leftA.distance;
140      });
141      list.forEach(function (row) {
142        if (row.children.length > 0) {
143          getList(row.children);
144        }
145      });
146    };
147    getList(currentLeftItem.children);
148  }
149
150  private retainsTableByShallowSize(currentLeftItem: ConstructorItem, sort: number): void {
151    const getList = function (list: Array<ConstructorItem>): void {
152      list.sort((leftA, rightB) => {
153        return sort === 1 ? leftA.shallowSize - rightB.shallowSize : rightB.shallowSize - leftA.shallowSize;
154      });
155      list.forEach(function (row) {
156        if (row.children.length > 0) {
157          getList(row.children);
158        }
159      });
160    };
161    getList(currentLeftItem.children);
162  }
163
164  private retainsTableByRetainedSize(currentLeftItem: ConstructorItem, sort: number): void {
165    const getList = function (list: Array<ConstructorItem>): void {
166      list.sort((leftA, rightB) => {
167        return sort === 1 ? leftA.retainedSize - rightB.retainedSize : rightB.retainedSize - leftA.retainedSize;
168      });
169      list.forEach(function (row) {
170        if (row.children.length > 0) {
171          getList(row.children);
172        }
173      });
174    };
175    getList(currentLeftItem.children);
176  }
177
178  private retainsTableByObjectName(currentLeftItem: ConstructorItem, sort: number): void {
179    const getList = function (list: Array<ConstructorItem>): void {
180      list.sort((leftA, rightB) => {
181        return sort === 1 ?
182          (`${leftA.objectName  }`).localeCompare(`${rightB.objectName  }`) :
183          (`${rightB.objectName  }`).localeCompare(`${leftA.objectName  }`);
184      });
185      list.forEach(function (row) {
186        if (row.children.length > 0) {
187          getList(row.children);
188        }
189      });
190    };
191    getList(currentLeftItem.children);
192  }
193
194  sortByLeftTable(column: string, sort: number): void {
195    switch (sort) {
196      case 0:
197        if (this.search!.value === '') {
198          this.tblSummary!.snapshotDataSource = this.summary;
199        } else {
200          this.tblSummary!.snapshotDataSource = this.summaryFilter;
201        }
202        break;
203      default:
204        if (this.search!.value === '') {
205          this.leftArray = [...this.summary];
206        } else {
207          this.leftArray = [...this.summaryFilter];
208        }
209        switch (column) {
210          case 'distance':
211            this.sortLeftByDistanceColum(sort);
212            break;
213          case 'shallowSize':
214            this.sortLeftByShallowSizeColum(sort);
215            break;
216          case 'retainedSize':
217            this.sortLeftByRetainedSizeColum(sort);
218            break;
219          case 'objectName':
220            this.sortLeftByObjectNameColum(sort);
221            break;
222        }
223        break;
224    }
225  }
226  private sortLeftByObjectNameColum(sort: number): void {
227    this.tblSummary!.snapshotDataSource = this.leftArray.sort((leftData, rightData) => {
228      return sort === 1 ?
229        (`${leftData.objectName  }`).localeCompare(`${rightData.objectName  }`) :
230        (`${rightData.objectName  }`).localeCompare(`${leftData.objectName  }`);
231    });
232    this.leftArray.forEach((currentLeftItem) => {
233      this.retainsTableByObjectName(currentLeftItem, sort);
234    });
235    this.tblSummary!.snapshotDataSource = this.leftArray;
236  }
237
238  private sortLeftByRetainedSizeColum(sort: number): void {
239    this.tblSummary!.snapshotDataSource = this.leftArray.sort((leftData, rightData) => {
240      return sort === 1 ?
241        leftData.retainedSize - rightData.retainedSize :
242        rightData.retainedSize - leftData.retainedSize;
243    });
244    this.leftArray.forEach((currentLeftItem) => {
245      this.retainsTableByRetainedSize(currentLeftItem, sort);
246    });
247    this.tblSummary!.snapshotDataSource = this.leftArray;
248  }
249
250  private sortLeftByShallowSizeColum(sort: number): void {
251    this.tblSummary!.snapshotDataSource = this.leftArray.sort((leftData, rightData) => {
252      return sort === 1 ?
253        leftData.shallowSize - rightData.shallowSize :
254        rightData.shallowSize - leftData.shallowSize;
255    });
256    this.leftArray.forEach((currentLeftItem) => {
257      this.retainsTableByShallowSize(currentLeftItem, sort);
258    });
259    this.tblSummary!.snapshotDataSource = this.leftArray;
260  }
261
262  private sortLeftByDistanceColum(sort: number): void {
263    this.tblSummary!.snapshotDataSource = this.leftArray.sort((leftData, rightData) => {
264      return sort === 1 ? leftData.distance - rightData.distance : rightData.distance - leftData.distance;
265    });
266    this.leftArray.forEach((currentLeftItem) => {
267      this.retainsTableByDistance(currentLeftItem, sort);
268    });
269    this.tblSummary!.snapshotDataSource = this.leftArray;
270  }
271
272  sortByRightTable(column: string, sort: number): void {
273    switch (sort) {
274      case 0:
275        this.tbs!.snapshotDataSource = this.retainsData;
276        break;
277      default:
278        this.rightArray = [...this.retainsData];
279        switch (column) {
280          case 'distance':
281            this.tbs!.snapshotDataSource = this.rightArray.sort((a, b) => {
282              return sort === 1 ? a.distance - b.distance : b.distance - a.distance;
283            });
284            this.rightArray.forEach((list) => {
285              this.retainsTableByDistance(list, sort);
286            });
287            this.tbs!.snapshotDataSource = this.rightArray;
288            break;
289          case 'shallowSize':
290            this.tbs!.snapshotDataSource = this.rightArray.sort((a, b) => {
291              return sort === 1 ? a.shallowSize - b.shallowSize : b.shallowSize - a.shallowSize;
292            });
293            this.rightArray.forEach((list) => {
294              this.retainsTableByShallowSize(list, sort);
295            });
296            this.tbs!.snapshotDataSource = this.rightArray;
297            break;
298          case 'retainedSize':
299            this.tbs!.snapshotDataSource = this.rightArray.sort((a, b) => {
300              return sort === 1 ? a.retainedSize - b.retainedSize : b.retainedSize - a.retainedSize;
301            });
302            this.rightArray.forEach((list) => {
303              this.retainsTableByRetainedSize(list, sort);
304            });
305            this.tbs!.snapshotDataSource = this.rightArray;
306            break;
307          case 'objectName':
308            this.tbs!.snapshotDataSource = this.rightArray.sort((a, b) => {
309              return sort === 1 ?
310                (`${a.objectName  }`).localeCompare(`${b.objectName  }`) :
311                (`${b.objectName  }`).localeCompare(`${a.objectName  }`);
312            });
313            this.rightArray.forEach((list) => {
314              this.retainsTableByObjectName(list, sort);
315            });
316            this.tbs!.snapshotDataSource = this.rightArray;
317            break;
318        }
319        break;
320    }
321  }
322
323  clickToggleTable(): void {
324    let lis = this.shadowRoot?.querySelectorAll<HTMLElement>('li');
325    let that = this;
326    lis!.forEach((li: HTMLElement, i: number) => {
327      lis![i].onclick = function (): void {
328        for (let i = 0; i < lis!.length; i++) {
329          lis![i].className = '';
330        }
331        switch (li.textContent) {
332          case 'Retainers':
333            that.stackTable!.style.display = 'none';
334            that.stackText!.style.display = 'none';
335            that.tbs!.style.display = 'flex';
336            that.tbs!.snapshotDataSource = that.retainsData;
337            break;
338          case 'Allocation stack':
339            if (that.stackData.length > 0) {
340              that.stackText!.style.display = 'none';
341              that.stackTable!.style.display = 'flex';
342              that.stackTable!.recycleDataSource = that.stackData;
343            } else {
344              that.stackText!.style.display = 'flex';
345              if (that.retainsData === undefined || that.retainsData.length === 0) {
346                that.stackText!.textContent = '';
347              } else {
348                that.stackText!.textContent =
349                  'Stack was not recorded for this object because it had been allocated before ' +
350                  'this profile recording started.';
351              }
352            }
353            that.tbs!.style.display = 'none';
354            break;
355        }
356        // @ts-ignore
357        this.className = 'active';
358      };
359    });
360  }
361
362  classFilter(): void {
363    this.search!.addEventListener('keyup', () => {
364      this.summaryFilter = [];
365      this.summaryData.forEach((a) => {
366        if (a.objectName.toLowerCase().includes(this.search!.value.toLowerCase())) {
367          this.summaryFilter.push(a);
368        } else {
369        }
370      });
371      this.tblSummary!.snapshotDataSource = this.summaryFilter;
372      let summaryTable = this.tblSummary!.shadowRoot?.querySelector('.table') as HTMLDivElement;
373      summaryTable.scrollTop = 0;
374      this.tblSummary!.reMeauseHeight();
375    });
376  }
377
378  clear(): void {
379    this.tbs!.snapshotDataSource = [];
380    this.stackTable!.recycleDataSource = [];
381    this.retainsData = [];
382    this.stackText!.textContent = '';
383    this.search!.value = '';
384    this.stack!.classList.remove('active');
385    this.tblTable!.scrollTop = 0;
386    this.stackTable!.style.display = 'none';
387    this.stackText!.style.display = 'none';
388    this.tbs!.style.display = 'flex';
389    this.rightTheadTable!.removeAttribute('sort');
390    this.leftTheadTable!.removeAttribute('sort');
391  }
392
393  connectedCallback(): void {
394    super.connectedCallback();
395    let filterHeight = 0;
396    let parentWidth = `${this.parentElement!.clientWidth  }px`;
397    let system = document
398      .querySelector('body > sp-application')
399      ?.shadowRoot?.querySelector('#app-content > sp-system-trace');
400    new ResizeObserver(() => {
401      let summaryPaneFilter = this.shadowRoot!.querySelector('#filter') as HTMLElement;
402      if (summaryPaneFilter.clientHeight > 0) {filterHeight = summaryPaneFilter.clientHeight}
403      if (this.parentElement!.clientHeight > filterHeight) {
404        summaryPaneFilter.style.display = 'flex';
405      } else {
406        summaryPaneFilter.style.display = 'none';
407      }
408      parentWidth = `${this.parentElement!.clientWidth  }px`;
409      this.tbs!.style.height = 'calc(100% - 30px)';
410      this.tbsTable!.style.width = `calc(${parentWidth} - ${this.leftTable!.style.width} - 5px)`;
411      this.tbs!.reMeauseHeight();
412      this.tblSummary!.reMeauseHeight();
413    }).observe(this.parentElement!);
414    new ResizeObserver(() => {
415      this.parentElement!.style.width = `${system!.clientWidth  }px`;
416      this.style.width = `${system!.clientWidth  }px`;
417    }).observe(system!);
418    new ResizeObserver(() => {
419      this.tbsTable!.style.width = `calc(${parentWidth} - ${this.leftTable!.style.width} - 5px)`;
420    }).observe(this.leftTable!);
421
422    this.tblSummary!.addEventListener('row-click', this.tblSummaryRowClick);
423    this.tbs!.addEventListener('row-click', this.tbsRowClick);
424    this.tblSummary!.addEventListener('icon-click', this.tblSummaryIconClick);
425    this.tbs!.addEventListener('icon-click', this.tbsIconClick);
426    this.tblSummary!.addEventListener('column-click', this.tblSummaryColumnClick);
427    this.tbs!.addEventListener('column-click', this.tblColumnClick);
428  }
429
430  disconnectedCallback(): void {
431    super.disconnectedCallback();
432    this.tblSummary!.removeEventListener('row-click', this.tblSummaryRowClick);
433    this.tbs!.removeEventListener('row-click', this.tbsRowClick);
434    this.tblSummary!.removeEventListener('icon-click', this.tblSummaryIconClick);
435    this.tbs!.removeEventListener('icon-click', this.tbsIconClick);
436    this.tblSummary!.removeEventListener('column-click', this.tblSummaryColumnClick);
437    this.tbs!.removeEventListener('column-click', this.tblColumnClick);
438  }
439
440  tblColumnClick = (evt: Event): void => {
441    // @ts-ignore
442    this.sortByRightTable(evt.detail.key, evt.detail.sort);
443    this.tbs!.reMeauseHeight();
444  };
445
446  tblSummaryColumnClick = (evt: Event): void => {
447    // @ts-ignore
448    this.sortByLeftTable(evt.detail.key, evt.detail.sort);
449    this.tblSummary!.reMeauseHeight();
450  };
451
452  tbsIconClick = (evt: Event): void => {
453    // @ts-ignore
454    let data = evt.detail.data;
455    if (data.status) {
456      data.getChildren();
457      let i = 0;
458      let retainsTable = (): void => {
459        const getList = (list: Array<ConstructorItem>): void => {
460          list.forEach((currentRow: ConstructorItem) => {
461            let shallow = `${Math.round((currentRow.shallowSize / this.fileSize) * 100)  }%`;
462            let retained = `${Math.round((currentRow.retainedSize / this.fileSize) * 100)  }%`;
463            currentRow.shallowPercent = shallow;
464            currentRow.retainedPercent = retained;
465            let nodeId = `${currentRow.nodeName  } @${currentRow.id}`;
466            currentRow.objectName = `${currentRow.edgeName  }\xa0` + 'in' + `\xa0${  nodeId}`;
467            if (currentRow.distance >= 100000000 || currentRow.distance === -5) {
468              // @ts-ignore
469              currentRow.distance = '-';
470            }
471            i++;
472            // @ts-ignore
473            if (i < evt.detail.data.distance - 1 && list[0].distance !== '-') {
474              list[0].getChildren();
475              list[0].expanded = false;
476              if (currentRow.hasNext) {
477                getList(currentRow.children);
478              }
479            } else {
480              return;
481            }
482          });
483        };
484        getList(data.children);
485      };
486      retainsTable();
487    } else {
488      data.status = true;
489    }
490    if (this.rightTheadTable!.hasAttribute('sort')) {
491      this.tbs!.snapshotDataSource = this.rightArray;
492    } else {
493      this.tbs!.snapshotDataSource = this.retainsData;
494    }
495    new ResizeObserver(() => {
496      if (this.parentElement?.clientHeight !== 0) {
497        this.tbs!.style.height = 'calc(100% - 30px)';
498        this.tbs!.reMeauseHeight();
499      }
500    }).observe(this.parentElement!);
501  };
502
503  tblSummaryIconClick = (evt: Event): void => {
504    // @ts-ignore
505    let data = evt.detail.data;
506    if (data.status) {
507      data.getChildren();
508      if (data.children.length > 0) {
509        data.children.sort(function (a: ConstructorItem, b: ConstructorItem) {
510          return b.retainedSize - a.retainedSize;
511        });
512        data.children.forEach((summaryDataEl: any) => {
513          let shallow = `${Math.round((summaryDataEl.shallowSize / this.fileSize) * 100)  }%`;
514          let retained = `${Math.round((summaryDataEl.retainedSize / this.fileSize) * 100)  }%`;
515          summaryDataEl.shallowPercent = shallow;
516          summaryDataEl.retainedPercent = retained;
517          if (summaryDataEl.distance >= 100000000 || summaryDataEl.distance === -5) {
518            summaryDataEl.distance = '-';
519          }
520          let nodeId = `${summaryDataEl.nodeName  } @${summaryDataEl.id}`;
521          summaryDataEl.nodeId = ` @${summaryDataEl.id}`;
522          if (data.isString()) {
523            summaryDataEl.objectName = `"${  summaryDataEl.nodeName  }"` + ` @${summaryDataEl.id}`;
524          } else {
525            summaryDataEl.objectName = nodeId;
526          }
527          if (summaryDataEl.edgeName !== '') {
528            summaryDataEl.objectName = `${summaryDataEl.edgeName  }\xa0` + '::' + `\xa0${  nodeId}`;
529          }
530        });
531      } else {
532        this.tblSummary!.snapshotDataSource = [];
533      }
534    } else {
535      data.status = true;
536    }
537    this.tblSummaryIconClickExtend();
538  };
539
540  tblSummaryIconClickExtend(): void {
541    if (this.search!.value !== '') {
542      if (this.leftTheadTable!.hasAttribute('sort')) {
543        this.tblSummary!.snapshotDataSource = this.leftArray;
544      } else {
545        this.tblSummary!.snapshotDataSource = this.summaryFilter;
546      }
547    } else {
548      if (this.leftTheadTable!.hasAttribute('sort')) {
549        this.tblSummary!.snapshotDataSource = this.leftArray;
550      } else {
551        this.tblSummary!.snapshotDataSource = this.summary;
552      }
553    }
554    new ResizeObserver(() => {
555      if (this.parentElement?.clientHeight !== 0) {
556        this.tblSummary!.style.height = '100%';
557        this.tblSummary!.reMeauseHeight();
558      }
559    }).observe(this.parentElement!);
560  }
561
562  tbsRowClick = (rowEvent: Event): void => {
563    // @ts-ignore
564    let data = rowEvent.detail.data as ConstructorItem;
565    (data as any).isSelected = true;
566    // @ts-ignore
567    if ((rowEvent.detail as any).callBack) {
568      // @ts-ignore
569      (rowEvent.detail as any).callBack(true);
570    }
571  };
572
573  tblSummaryRowClick = (evt: Event): void => {
574    this.rightTheadTable!.removeAttribute('sort');
575    this.tbsTable!.scrollTop = 0;
576    //@ts-ignore
577    let data = evt.detail.data as ConstructorItem;
578    (data as any).isSelected = true;
579    this.initRetainsData(data);
580    if (this.retainsData.length > 0) {
581      if (this.retainsData[0].distance > 1) {
582        this.retainsData[0].getChildren();
583        this.retainsData[0].expanded = false;
584      }
585      let i = 0;
586      let that = this;
587      let retainsTable = (): void => {
588        const getList = (list: Array<ConstructorItem>): void => {
589          list.forEach((summaryRow: ConstructorItem) => {
590            let retainsShallow = `${Math.round((summaryRow.shallowSize / this.fileSize) * 100)  }%`;
591            let retained = `${Math.round((summaryRow.retainedSize / this.fileSize) * 100)  }%`;
592            summaryRow.shallowPercent = retainsShallow;
593            summaryRow.retainedPercent = retained;
594            let nodeId = `${summaryRow.nodeName  } @${summaryRow.id}`;
595            summaryRow.objectName = `${summaryRow.edgeName  }\xa0` + 'in' + `\xa0${  nodeId}`;
596            if (summaryRow.distance >= 100000000 || summaryRow.distance === -5) {
597              //@ts-ignore
598              summaryRow.distance = '-';
599            }
600            i++;
601            //@ts-ignore
602            if (i < that.retainsData[0].distance - 1 && list[0].distance !== '-') {
603              list[0].getChildren();
604              list[0].expanded = false;
605              if (summaryRow.hasNext) {
606                getList(summaryRow.children);
607              }
608            } else {
609              return;
610            }
611          });
612        };
613        getList(that.retainsData[0].children);
614      };
615      retainsTable();
616      this.tbs!.snapshotDataSource = this.retainsData;
617    } else {
618      this.tbs!.snapshotDataSource = [];
619    }
620    this.tblSummaryRowClickExtend(data);
621  };
622
623  private initRetainsData(data: ConstructorItem): void {
624    this.retainsData = [];
625    this.retainsData = HeapDataInterface.getInstance().getRetains(data);
626    this.retainsData.forEach((element) => {
627      let shallow = `${Math.round((element.shallowSize / this.fileSize) * 100)  }%`;
628      let retained = `${Math.round((element.retainedSize / this.fileSize) * 100)  }%`;
629      element.shallowPercent = shallow;
630      element.retainedPercent = retained;
631      if (element.distance >= 100000000 || element.distance === -5) {
632        //@ts-ignore
633        element.distance = '-';
634      }
635      let nodeId = `${element.nodeName  } @${element.id}`;
636      element.objectName = `${element.edgeName  }\xa0` + 'in' + `\xa0${  nodeId}`;
637    });
638  }
639
640  private tblSummaryRowClickExtend(data: ConstructorItem): void {
641    if (this.file!.name.includes('Timeline')) {
642      this.stackData = HeapDataInterface.getInstance().getAllocationStackData(data);
643      if (this.stackData.length > 0) {
644        this.stackTable!.recycleDataSource = this.stackData;
645        this.stackText!.textContent = '';
646        this.stackText!.style.display = 'none';
647        if (this.stack!.className === 'active') {
648          this.stackTable!.style.display = 'grid';
649          this.tbs!.style.display = 'none';
650        }
651      } else {
652        this.stackText!.style.display = 'flex';
653        this.stackTable!.recycleDataSource = [];
654        this.stackTable!.style.display = 'none';
655        if (this.retainers!.className === 'active') {
656          this.stackText!.style.display = 'none';
657        }
658        if (this.retainsData === undefined || this.retainsData.length === 0) {
659          this.stackText!.textContent = '';
660        } else {
661          this.stackText!.textContent =
662            'Stack was not recorded for this object because it had been allocated before ' +
663            'this profile recording started.';
664        }
665      }
666    }
667    new ResizeObserver(() => {
668      this.tbs!.style.height = 'calc(100% - 30px)';
669      this.tbs!.reMeauseHeight();
670      this.stackTable!.style.height = 'calc(100% - 30px)';
671      this.stackTable!.reMeauseHeight();
672    }).observe(this.parentElement!);
673    // @ts-ignore
674    if ((evt.detail as any).callBack) {
675      // @ts-ignore
676      (evt.detail as any).callBack(true);
677    }
678  }
679
680  initHtml(): string {
681    return TabPaneSummaryHtml;
682  }
683}
684