• 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 { element } from '../../../../base-ui/BaseElement.js';
17import { TimeRange } from '../timer-shaft/RangeRuler.js';
18import '../../../../base-ui/icon/LitIcon.js';
19import { Rect } from '../timer-shaft/Rect.js';
20import { BaseStruct } from '../../../bean/BaseStruct.js';
21import { SpSystemTrace } from '../../SpSystemTrace.js';
22import { ns2x } from '../TimerShaftElement.js';
23import { TraceRowObject } from './TraceRowObject.js';
24import { LitCheckBox } from '../../../../base-ui/checkbox/LitCheckBox.js';
25import { LitIcon } from '../../../../base-ui/icon/LitIcon';
26import '../../../../base-ui/popover/LitPopoverV.js';
27import { LitPopover } from '../../../../base-ui/popover/LitPopoverV.js';
28import { info } from '../../../../log/Log.js';
29import { ColorUtils } from './ColorUtils.js';
30import { drawSelectionRange } from '../../../database/ui-worker/ProcedureWorkerCommon.js';
31import { TraceRowConfig } from './TraceRowConfig.js';
32
33export class RangeSelectStruct {
34  startX: number | undefined;
35  endX: number | undefined;
36  startNS: number | undefined;
37  endNS: number | undefined;
38}
39
40let collectList: Array<any> = [];
41let rowDragElement: EventTarget | undefined | null;
42let dragDirection: string = '';
43
44@element('trace-row')
45export class TraceRow<T extends BaseStruct> extends HTMLElement {
46  static ROW_TYPE_CPU = 'cpu-data';
47  static ROW_TYPE_CPU_STATE = 'cpu-state';
48  static ROW_TYPE_CPU_FREQ = 'cpu-freq';
49  static ROW_TYPE_CPU_FREQ_LIMIT = 'cpu-limit-freq';
50  static ROW_TYPE_FPS = 'fps';
51  static ROW_TYPE_NATIVE_MEMORY = 'native-memory';
52  static ROW_TYPE_HIPERF = 'hiperf';
53  static ROW_TYPE_DELIVER_INPUT_EVENT = 'DeliverInputEvent';
54  static ROW_TYPE_HIPERF_CPU = 'hiperf-cpu';
55  static ROW_TYPE_HIPERF_PROCESS = 'hiperf-process';
56  static ROW_TYPE_HIPERF_THREAD = 'hiperf-thread';
57  static ROW_TYPE_HIPERF_REPORT = 'hiperf-report';
58  static ROW_TYPE_HIPERF_EVENT = 'hiperf-event';
59  static ROW_TYPE_PROCESS = 'process';
60  static ROW_TYPE_APP_STARTUP = 'app-startup';
61  static ROW_TYPE_STATIC_INIT = 'static-init';
62  static ROW_TYPE_THREAD = 'thread';
63  static ROW_TYPE_MEM = 'mem';
64  static ROW_TYPE_VIRTUAL_MEMORY_GROUP = 'virtual-memory-group';
65  static ROW_TYPE_VIRTUAL_MEMORY = 'virtual-memory-cell';
66  static ROW_TYPE_FILE_SYSTEM_GROUP = 'file-system-group';
67  static ROW_TYPE_FILE_SYSTEM = 'file-system-cell';
68  static ROW_TYPE_HEAP = 'heap';
69  static ROW_TYPE_JS_MEMORY = 'js-memory';
70  static ROW_TYPE_HEAP_SNAPSHOT = 'heap-snapshot';
71  static ROW_TYPE_HEAP_TIMELINE = 'heap-timeline';
72  static ROW_TYPE_FUNC = 'func';
73  static ROW_TYPE_MONITOR = 'ability-monitor';
74  static ROW_TYPE_CPU_ABILITY = 'cpu-ability';
75  static ROW_TYPE_MEMORY_ABILITY = 'memory-ability';
76  static ROW_TYPE_DISK_ABILITY = 'disk-ability';
77  static ROW_TYPE_NETWORK_ABILITY = 'network-ability';
78  static ROW_TYPE_SDK = 'sdk';
79  static ROW_TYPE_SDK_COUNTER = 'sdk-counter';
80  static ROW_TYPE_SDK_SLICE = 'sdk-slice';
81  static ROW_TYPE_ENERGY = 'energy';
82  static ROW_TYPE_ANOMALY_ENERGY = 'anomaly-energy';
83  static ROW_TYPE_SYSTEM_ENERGY = 'system-energy';
84  static ROW_TYPE_POWER_ENERGY = 'power-energy';
85  static ROW_TYPE_STATE_ENERGY = 'state-energy';
86  static ROW_TYPE_SMAPS = 'smaps';
87  static ROW_TYPE_CLOCK_GROUP = 'clock-group';
88  static ROW_TYPE_CLOCK = 'clock';
89  static ROW_TYPE_IRQ_GROUP = 'irq-group';
90  static ROW_TYPE_IRQ = 'irq';
91  static ROW_TYPE_JANK = 'janks';
92  static FRAME_WIDTH: number = 0;
93  static range: TimeRange | undefined | null;
94  static rangeSelectObject: RangeSelectStruct | undefined;
95  public obj: TraceRowObject<any> | undefined | null;
96  isHover: boolean = false;
97  hoverX: number = 0;
98  hoverY: number = 0;
99  index: number = 0;
100  public must: boolean = false;
101  public isTransferCanvas = false;
102  onComplete: Function | undefined;
103  isComplete: boolean = false;
104  public dataList: Array<T> = [];
105  public dataList2: Array<T> = [];
106  public dataListCache: Array<T> = [];
107  public describeEl: HTMLElement | null | undefined;
108  public canvas: Array<HTMLCanvasElement> = [];
109  public canvasContainer: HTMLDivElement | null | undefined;
110  public tipEL: HTMLDivElement | null | undefined;
111  public checkBoxEL: LitCheckBox | null | undefined;
112  public collectEL: LitIcon | null | undefined;
113  public onThreadHandler: ((useCache: boolean, buf: ArrayBuffer | undefined | null) => void) | undefined | null;
114  public onDrawTypeChangeHandler: ((type: number) => void) | undefined | null;
115  public supplier: (() => Promise<Array<T>>) | undefined | null;
116  public favoriteChangeHandler: ((fav: TraceRow<any>) => void) | undefined | null;
117  public selectChangeHandler: ((list: Array<TraceRow<any>>) => void) | undefined | null;
118  dpr = window.devicePixelRatio || 1;
119  // @ts-ignore
120  offscreen: Array<OffscreenCanvas | undefined> = [];
121  canvasWidth = 0;
122  canvasHeight = 0;
123  public _frame: Rect | undefined;
124  public isLoading: boolean = false;
125  public readonly args: any;
126  public templateType: Array<string> = [];
127  private rootEL: HTMLDivElement | null | undefined;
128  private nameEL: HTMLLabelElement | null | undefined;
129  private _rangeSelect: boolean = false;
130  private _drawType: number = 0;
131  private folderIconEL: LitIcon | null | undefined;
132  online: boolean = false;
133  static isUserInteraction: boolean;
134  asyncFuncName: string | undefined | null;
135  asyncFuncNamePID: number | undefined | null;
136  translateY: number = 0; //single canvas offsetY;
137  childrenList: Array<TraceRow<any>> = [];
138  parentRowEl: TraceRow<any> | undefined;
139  focusHandler?: (ev: MouseEvent) => void | undefined;
140
141  constructor(
142    args: {
143      canvasNumber: number;
144      alpha: boolean;
145      contextId: string;
146      isOffScreen: boolean;
147      skeleton?: boolean;
148    } = {
149      canvasNumber: 1,
150      alpha: false,
151      contextId: '2d',
152      isOffScreen: true,
153      skeleton: false,
154    }
155  ) {
156    super();
157    this.args = args;
158    this.attachShadow({ mode: 'open' }).innerHTML = this.initHtml();
159    this.initElements();
160  }
161
162  static skeleton<T extends BaseStruct>(): TraceRow<T> {
163    let tr = new TraceRow<T>({
164      alpha: false,
165      canvasNumber: 0,
166      contextId: '',
167      isOffScreen: false,
168      skeleton: true,
169    });
170    tr.isTransferCanvas = true;
171    return tr;
172  }
173
174  static get observedAttributes() {
175    return [
176      'folder',
177      'name',
178      'expansion',
179      'children',
180      'height',
181      'row-type',
182      'row-id',
183      'row-parent-id',
184      'sleeping',
185      'check-type',
186      'collect-type',
187      'disabled-check',
188      'row-discard',
189    ];
190  }
191
192  get hasParentRowEl(): boolean {
193    return this.parentRowEl !== undefined;
194  }
195
196  get rowDiscard(): boolean {
197    return this.hasAttribute('row-discard');
198  }
199
200  set rowDiscard(value: boolean) {
201    if (value) {
202      this.setAttribute('row-discard', '');
203      this.style.display = 'none';
204    } else {
205      this.removeAttribute('row-discard');
206      this.style.display = 'block';
207    }
208  }
209
210  get collect() {
211    return this.hasAttribute('collect-type');
212  }
213
214  set collect(value) {
215    if (value) {
216      this.setAttribute('collect-type', '');
217    } else {
218      this.removeAttribute('collect-type');
219    }
220  }
221
222  get rangeSelect(): boolean {
223    return this._rangeSelect;
224  }
225
226  set rangeSelect(value: boolean) {
227    this._rangeSelect = value;
228  }
229
230  sleeping: boolean = false;
231
232  get rowType(): string | undefined | null {
233    return this.getAttribute('row-type');
234  }
235
236  set rowType(val) {
237    this.setAttribute('row-type', val || '');
238  }
239
240  get rowId(): string | undefined | null {
241    return this.getAttribute('row-id');
242  }
243
244  set rowId(val) {
245    this.setAttribute('row-id', val || '');
246  }
247
248  get rowParentId(): string | undefined | null {
249    return this.getAttribute('row-parent-id');
250  }
251
252  set rowParentId(val) {
253    this.setAttribute('row-parent-id', val || '');
254  }
255
256  set rowHidden(val: boolean) {
257    if (val) {
258      this.setAttribute('row-hidden', '');
259    } else {
260      this.removeAttribute('row-hidden');
261    }
262  }
263
264  get name(): string {
265    return this.getAttribute('name') || '';
266  }
267
268  set name(value: string) {
269    this.setAttribute('name', value);
270  }
271
272  get folder(): boolean {
273    return this.hasAttribute('folder');
274  }
275
276  set folder(value: boolean) {
277    if (value) {
278      this.setAttribute('folder', '');
279    } else {
280      this.removeAttribute('folder');
281    }
282  }
283
284  get expansion(): boolean {
285    return this.hasAttribute('expansion');
286  }
287
288  set expansion(value) {
289    if (value === this.expansion) {
290      return;
291    }
292    let fragment: DocumentFragment | undefined  = document.createDocumentFragment();
293    if (value) {
294      this.childrenList.forEach((child: any) => {
295        child.rowHidden = false;
296        fragment!.appendChild(child);
297      });
298      this.insertAfter(fragment, this);
299    } else {
300      this.childrenList.length = 0;
301      this.parentElement?.querySelectorAll<any>(`[row-parent-id='${this.rowId!}']`).forEach((it) => {
302        this.childrenList.push(it);
303        if (it.folder) {
304          it.expansion = value;
305        }
306        fragment!.appendChild(it);
307      });
308      this.childrenList.forEach(child => {
309        fragment!.removeChild(child);
310      });
311    }
312    fragment = undefined;
313    if (value) {
314      this.setAttribute('expansion', '');
315    } else {
316      this.removeAttribute('expansion');
317    }
318    this.dispatchEvent(
319      new CustomEvent('expansion-change', {
320        detail: {
321          expansion: this.expansion,
322          rowType: this.rowType,
323          rowId: this.rowId,
324          rowParentId: this.rowParentId,
325        },
326      })
327    );
328  }
329
330  clearMemory() {
331    this.dataList2 = [];
332    this.dataList = [];
333    this.dataListCache = [];
334    if (this.rootEL) {
335      this.rootEL.innerHTML = ''
336    }
337    if (this.folder) {
338      this.childrenList.forEach(child => {
339        if (child.clearMemory !== undefined) {
340          child.clearMemory();
341        }
342      })
343      this.childrenList = [];
344    }
345  }
346
347  addTemplateTypes(...type: string[]) {
348    this.templateType.push(...type);
349  }
350
351  replaceTraceRow(newNode: any, oldNode: any) {
352    let oldIndex = this.childrenList.indexOf(oldNode);
353    if (oldIndex != -1) {
354      this.childrenList.splice(oldIndex, 1, newNode);
355    }
356  }
357
358  toParentAddTemplateType = (currentRowEl: TraceRow<any>) => {
359    let parentRow = currentRowEl.parentRowEl;
360    if (parentRow !== undefined) {
361      parentRow.templateType.push(...currentRowEl.templateType);
362      if (parentRow.parentRowEl !== undefined) {
363        this.toParentAddTemplateType(parentRow);
364      }
365    }
366  };
367
368  addChildTraceRow(child: TraceRow<any>) {
369    TraceRowConfig.allTraceRowList.push(child);
370    child.parentRowEl = this;
371    this.toParentAddTemplateType(child);
372    child.setAttribute('scene', '');
373    this.childrenList.push(child);
374  }
375
376  addChildTraceRowAfter(child: TraceRow<any>, targetRow: TraceRow<any>) {
377    TraceRowConfig.allTraceRowList.push(child);
378    child.parentRowEl = this;
379    let index = this.childrenList.indexOf(targetRow);
380    child.setAttribute('scene', '');
381    if (index != -1) {
382      this.childrenList.splice(index + 1, 0, child);
383    } else {
384      this.childrenList.push(child);
385    }
386  }
387
388  addChildTraceRowSpecifyLocation(child: TraceRow<any>, index: number) {
389    TraceRowConfig.allTraceRowList.push(child);
390    child.parentRowEl = this;
391    child.setAttribute('scene', '');
392    this.childrenList.splice(index, 0, child);
393  }
394  insertAfter(newEl: DocumentFragment, targetEl: HTMLElement) {
395    let parentEl = targetEl.parentNode;
396    if (parentEl!.lastChild == targetEl) {
397      parentEl!.appendChild(newEl);
398    } else {
399      parentEl!.insertBefore(newEl, targetEl.nextSibling);
400    }
401  }
402
403  set tip(value: string) {
404    if (this.tipEL) {
405      this.tipEL.innerHTML = value;
406    }
407  }
408
409  get frame(): Rect | any {
410    if (this._frame) {
411      this._frame.width = TraceRow.FRAME_WIDTH
412      this._frame.height = this.clientHeight;
413      return this._frame;
414    } else {
415      this._frame = new Rect(
416        0,
417        0,
418        TraceRow.FRAME_WIDTH,
419        this.clientHeight || 40
420      );
421      return this._frame;
422    }
423  }
424
425  set frame(f: Rect) {
426    this._frame = f;
427  }
428
429  get disabledCheck(): boolean {
430    return this.hasAttribute('disabled-check');
431  }
432
433  set disabledCheck(value: boolean) {
434    if (value) {
435      this.setAttribute('disabled-check', '');
436      this.checkBoxEL!.style.display = 'none';
437    } else {
438      this.removeAttribute('disabled-check');
439      this.checkBoxEL!.style.display = 'flex';
440    }
441  }
442
443  get checkType(): string {
444    return this.getAttribute('check-type') || '';
445  }
446
447  set checkType(value: string) {
448    if (!value || value.length == 0) {
449      this.removeAttribute('check-type');
450      return;
451    }
452    if (this.getAttribute('check-type') === value) {
453      return;
454    }
455    if (this.folder) {
456      this.childrenList.forEach((it) => (it.checkType = value));
457    }
458    this.setAttribute('check-type', value);
459    if (this.hasAttribute('disabled-check')) {
460      this.checkBoxEL!.style.display = 'none';
461      return;
462    }
463    switch (value) {
464      case '-1':
465        this.checkBoxEL!.style.display = 'none';
466        this.rangeSelect = false;
467        break;
468      case '0':
469        this.checkBoxEL!.style.display = 'flex';
470        this.checkBoxEL!.checked = false;
471        this.checkBoxEL!.indeterminate = false;
472        this.rangeSelect = false;
473        break;
474      case '1':
475        this.checkBoxEL!.style.display = 'flex';
476        this.checkBoxEL!.checked = false;
477        this.checkBoxEL!.indeterminate = true;
478        this.rangeSelect = false;
479        break;
480      case '2':
481        this.rangeSelect = true;
482        this.checkBoxEL!.style.display = 'flex';
483        this.checkBoxEL!.checked = true;
484        this.checkBoxEL!.indeterminate = false;
485        break;
486    }
487  }
488
489  get drawType(): number {
490    return this._drawType;
491  }
492
493  set drawType(value: number) {
494    this._drawType = value;
495    let radioList: NodeListOf<any> = this.shadowRoot!.querySelectorAll('input[type=radio][name=status]');
496    if (radioList!.length > 0) {
497      radioList[Number(value)].checked = true;
498    }
499  }
500
501  get highlight(): boolean {
502    return this.hasAttribute('expansion');
503  }
504
505  set highlight(value: boolean) {
506    if (value) {
507      this.setAttribute('highlight', '');
508    } else {
509      this.removeAttribute('highlight');
510    }
511  }
512
513  set folderPaddingLeft(value: number) {
514    this.folderIconEL!.style.marginLeft = value + 'px';
515  }
516
517  initElements(): void {
518    this.rootEL = this.shadowRoot?.querySelector('.root');
519    this.checkBoxEL = this.shadowRoot?.querySelector<LitCheckBox>('.lit-check-box');
520    this.collectEL = this.shadowRoot?.querySelector<LitIcon>('.collect');
521    this.describeEl = this.shadowRoot?.querySelector('.describe');
522    this.folderIconEL = this.shadowRoot?.querySelector<LitIcon>('.icon');
523    this.nameEL = this.shadowRoot?.querySelector('.name');
524    this.canvasContainer = this.shadowRoot?.querySelector('.panel-container');
525    this.tipEL = this.shadowRoot?.querySelector('.tip');
526    let canvasNumber = this.args['canvasNumber'];
527    if (!this.args['skeleton']) {
528      for (let i = 0; i < canvasNumber; i++) {
529        let canvas = document.createElement('canvas');
530        canvas.className = 'panel';
531        this.canvas.push(canvas);
532        if (this.canvasContainer) {
533          this.canvasContainer.appendChild(canvas);
534        }
535      }
536    }
537    this.describeEl?.addEventListener('click', () => {
538      if (this.folder) {
539        this.expansion = !this.expansion;
540      }
541    });
542  }
543
544  initCanvas(list: Array<HTMLCanvasElement>): void {
545    let timerShaftEL = document!
546      .querySelector('body > sp-application')!
547      .shadowRoot!.querySelector('#sp-system-trace')!
548      .shadowRoot!.querySelector('div > timer-shaft-element');
549    let timerShaftCanvas = timerShaftEL!.shadowRoot!.querySelector<HTMLCanvasElement>('canvas');
550    let tempHeight: number = 0;
551    if (this.rowType == TraceRow.ROW_TYPE_FUNC) {
552      tempHeight = 20;
553    } else if (this.rowType == TraceRow.ROW_TYPE_THREAD) {
554      tempHeight = 30;
555    } else if (this.rowType == TraceRow.ROW_TYPE_SYSTEM_ENERGY) {
556      tempHeight = 80;
557    } else if (this.rowType == TraceRow.ROW_TYPE_POWER_ENERGY) {
558      tempHeight = 200;
559    } else if (this.rowType == TraceRow.ROW_TYPE_ANOMALY_ENERGY) {
560      tempHeight = 55;
561    } else {
562      tempHeight = 40;
563    }
564    this.dpr = window.devicePixelRatio || 1;
565    list.forEach((canvas, i) => {
566      this.rootEL!.style.height = `${this.getAttribute('height') || '40'}px`;
567      canvas.style.width = timerShaftCanvas!.style.width;
568      canvas.style.height = tempHeight + 'px';
569      this.canvasWidth = timerShaftCanvas!.width;
570      this.canvasHeight = Math.ceil(tempHeight * this.dpr);
571      canvas.width = this.canvasWidth;
572      canvas.height = this.canvasHeight;
573      // @ts-ignore
574      this.offscreen.push(canvas!.transferControlToOffscreen());
575    });
576  }
577
578  updateWidth(width: number) {
579    this.dpr = window.devicePixelRatio || 1;
580    let tempHeight: number = 0;
581    if (this.rowType == TraceRow.ROW_TYPE_FUNC) {
582      tempHeight = 20;
583    } else if (this.rowType == TraceRow.ROW_TYPE_THREAD) {
584      tempHeight = 30;
585    } else if (this.rowType == TraceRow.ROW_TYPE_SYSTEM_ENERGY) {
586      tempHeight = 90;
587    } else if (this.rowType == TraceRow.ROW_TYPE_POWER_ENERGY) {
588      tempHeight = 200;
589    } else if (this.rowType == TraceRow.ROW_TYPE_ANOMALY_ENERGY) {
590      tempHeight = 55;
591    } else {
592      tempHeight = 40;
593    }
594    if (this.canvas.length > 1) {
595      tempHeight = 20;
596    }
597    this.canvas.forEach((it) => {
598      this.canvasWidth = Math.ceil((width - (this.describeEl?.clientWidth || 248)) * this.dpr);
599      this.canvasHeight = Math.ceil(tempHeight * this.dpr);
600      it!.style.width = width - (this.describeEl?.clientWidth || 248) + 'px';
601      if (this.args.isOffScreen) {
602        this.draw(true);
603      }
604    });
605  }
606
607  drawLine(item: HTMLDivElement, direction: string /*string[top|bottom]*/) {
608    if (!item) return;
609    switch (direction) {
610      case 'top':
611        item.classList.remove('line-bottom');
612        item.classList.add('line-top');
613        break;
614      case 'bottom':
615        item.classList.remove('line-top');
616        item.classList.add('line-bottom');
617        break;
618      case '':
619        item.classList.remove('line-top');
620        item.classList.remove('line-bottom');
621        break;
622    }
623  }
624
625  connectedCallback() {
626    this.checkBoxEL!.onchange = (ev: any) => {
627      info('checkBoxEL onchange ');
628      if (!ev.target.checked) {
629        info('checkBoxEL target not checked');
630        this.rangeSelect = false;
631        this.checkType = '0';
632      } else {
633        this.rangeSelect = true;
634        this.checkType = '2';
635      }
636      this.setCheckBox(ev.target.checked);
637    };
638    this.describeEl!.ondragstart = (ev: DragEvent) => this.rowDragstart(ev);
639    this.describeEl!.ondragleave = (ev: any) => {
640      this.drawLine(ev.currentTarget, '');
641      return undefined;
642    };
643    this.describeEl!.ondragend = (ev: any) => {
644      rowDragElement = null;
645      ev.target.classList.remove('drag');
646      this.drawLine(ev.currentTarget, '');
647      return undefined;
648    };
649    this.describeEl!.ondragover = (ev: any) => {
650      if (!this.collect) return;
651      if (rowDragElement === this) return;
652      let rect = ev.currentTarget.getBoundingClientRect();
653      if (ev.clientY >= rect.top && ev.clientY < rect.top + rect.height / 2) {
654        //上面
655        dragDirection = 'top';
656        this.drawLine(ev.currentTarget, 'top');
657      } else if (ev.clientY <= rect.bottom && ev.clientY > rect.top + rect.height / 2) {
658        //下面
659        dragDirection = 'bottom';
660        this.drawLine(ev.currentTarget, 'bottom');
661      }
662      return undefined;
663    };
664    this.describeEl!.ondrop = (ev: any) => {
665      if (!this.collect) return;
666      this.drawLine(ev.currentTarget, '');
667      let spacer = this.parentElement!.previousElementSibling! as HTMLDivElement;
668      let startDragNode = collectList.findIndex((it) => it === rowDragElement);
669      let endDragNode = collectList.findIndex((it) => it === this);
670      if (startDragNode === -1 || endDragNode === -1) return;
671      if (startDragNode < endDragNode && dragDirection === 'top') {
672        endDragNode--;
673      } else if (startDragNode > endDragNode && dragDirection === 'bottom') {
674        endDragNode++;
675      }
676      collectList.splice(endDragNode, 0, ...collectList.splice(startDragNode, 1));
677      collectList.forEach((it, i) => {
678        if (i == 0) {
679          it.style.top = `${spacer.offsetTop + 48}px`;
680        } else {
681          it.style.top = `${collectList[i - 1].offsetTop + collectList[i - 1].offsetHeight}px`;
682        }
683      });
684    };
685    this.collectEL!.onclick = (e) => {
686      if (this.isComplete) {
687        this.collect = !this.collect;
688        if (this.collect) {
689          this.describeEl!.draggable = false;
690        } else {
691          this.describeEl!.draggable = false;
692        }
693        document.dispatchEvent(
694          new CustomEvent('collect', {
695            detail: {
696              type: e.type,
697              row: this,
698            },
699          })
700        );
701        this.favoriteChangeHandler?.(this);
702      }
703    };
704    if (!this.args['skeleton']) {
705      this.initCanvas(this.canvas);
706    }
707    let radioList = this.shadowRoot!.querySelectorAll('input[type=radio][name=status]');
708    let popover = this.shadowRoot!.querySelector<LitPopover>('.popover');
709    this.shadowRoot?.querySelector<HTMLDivElement>('#first-radio')?.addEventListener('click', (e) => {
710      // @ts-ignore
711      radioList[0]!.checked = true;
712      // @ts-ignore
713      popover!.visible = false;
714      setTimeout(() => {
715        this.onDrawTypeChangeHandler?.(0);
716      }, 300);
717    });
718    this.shadowRoot?.querySelector<HTMLDivElement>('#second-radio')?.addEventListener('click', (e) => {
719      // @ts-ignore
720      radioList[1]!.checked = true;
721      // @ts-ignore
722      popover!.visible = false;
723      setTimeout(() => {
724        this.onDrawTypeChangeHandler?.(1);
725      }, 300);
726    });
727  }
728
729  rowDragstart(ev: any) {
730    rowDragElement = this;
731    ev.target.classList.add('drag');
732  }
733
734  setCheckBox(isCheck: boolean) {
735    if (this.folder) {
736      // favorite row  check change;
737      window.publish(window.SmartEvent.UI.CheckALL, {
738        rowId: this.rowId,
739        isCheck: isCheck,
740      });
741      this.childrenList!.forEach((ck) => {
742        ck.setAttribute('check-type', isCheck ? '2' : '0');
743        let allCheck: LitCheckBox | null | undefined = ck?.shadowRoot?.querySelector('.lit-check-box');
744        if (allCheck) {
745          allCheck!.checked = isCheck;
746        }
747      });
748    } else if (this.rowParentId == '' && !this.folder) {
749      let traceRowList: Array<TraceRow<any>> = [];
750      this.parentElement!.parentElement!.querySelectorAll<TraceRow<any>>("trace-row[check-type='2'][folder]").forEach(
751        (it) => {
752          traceRowList.push(
753            ...it.childrenList.filter((it) => {
754              return it.checkType === '2';
755            })
756          );
757        }
758      );
759      this.selectChangeHandler?.([
760        ...this.parentElement!.querySelectorAll<TraceRow<any>>("trace-row[check-type='2']"),
761        ...traceRowList,
762      ]);
763      return;
764    }
765    let checkList = this.parentElement!.parentElement!.querySelectorAll<TraceRow<any>>(
766      `trace-row[row-parent-id='${this.folder ? this.rowId : this.rowParentId}'][check-type="2"]`
767    );
768    let checkList2: Array<TraceRow<any>> = [];
769    if (this.folder && !this.expansion) {
770      checkList2 = this.childrenList.filter((it) => {
771        return it.checkType === '2';
772      });
773    }
774    let unselectedList = this.parentElement!.parentElement!.querySelectorAll<TraceRow<any>>(
775      `trace-row[row-parent-id='${this.folder ? this.rowId : this.rowParentId}'][check-type="0"]`
776    );
777    let unselectedList2: Array<TraceRow<any>> = [];
778    if (this.folder && !this.expansion) {
779      unselectedList2 = this.childrenList.filter((it) => {
780        return it.checkType === '0';
781      });
782    }
783    let parentCheck: LitCheckBox | null | undefined = this.parentRowEl?.shadowRoot?.querySelector('.lit-check-box');
784    if (unselectedList?.length == 0 && unselectedList.length === 0) {
785      this.parentRowEl?.setAttribute('check-type', '2');
786      if (parentCheck) {
787        parentCheck!.checked = true;
788        parentCheck!.indeterminate = false;
789      }
790      checkList?.forEach((rowItem) => {
791        rowItem.checkType = '2';
792        rowItem.rangeSelect = true;
793      });
794      checkList2?.forEach((it) => {
795        it.checkType = '2';
796        it.rangeSelect = true;
797      });
798    } else {
799      this.parentRowEl?.setAttribute('check-type', '1');
800      if (parentCheck) {
801        parentCheck!.checked = false;
802        parentCheck!.indeterminate = true;
803      }
804      checkList?.forEach((it) => {
805        it.checkType = '2';
806        it.rangeSelect = true;
807      });
808      checkList2?.forEach((it) => {
809        it.checkType = '2';
810        it.rangeSelect = true;
811      });
812      unselectedList?.forEach((item) => {
813        item.checkType = '0';
814        item.rangeSelect = false;
815      });
816      unselectedList2?.forEach((it) => {
817        it.checkType = '0';
818        it.rangeSelect = false;
819      });
820    }
821
822    if (checkList?.length == 0 && checkList2?.length === 0) {
823      this.parentRowEl?.setAttribute('check-type', '0');
824      if (parentCheck) {
825        parentCheck!.checked = false;
826        parentCheck!.indeterminate = false;
827      }
828      unselectedList?.forEach((it) => {
829        it.checkType = '0';
830        it.rangeSelect = false;
831      });
832      unselectedList2?.forEach((it) => {
833        it.checkType = '0';
834        it.rangeSelect = false;
835      });
836    }
837    let traceRowList: Array<TraceRow<any>> = [];
838    this.parentElement!.parentElement!.querySelectorAll<TraceRow<any>>("trace-row[check-type='2'][folder]").forEach(
839      (it) => {
840        traceRowList.push(
841          ...it.childrenList.filter((it) => {
842            return it.checkType === '2';
843          })
844        );
845      }
846    );
847    this.selectChangeHandler?.([
848      ...this.parentElement!.parentElement!.querySelectorAll<TraceRow<any>>("trace-row[check-type='2']"),
849      ...traceRowList,
850    ]);
851  }
852
853  onMouseHover(x: number, y: number, tip: boolean = true): T | undefined | null {
854    if (this.tipEL) {
855      this.tipEL.style.display = 'none';
856    }
857    return null;
858  }
859
860  setTipLeft(x: number, struct: any) {
861    if (struct == null && this.tipEL) {
862      this.tipEL.style.display = 'none';
863      return;
864    }
865    if (this.tipEL) {
866      this.tipEL.style.display = 'flex';
867      if (x + this.tipEL.clientWidth > (this.canvasContainer!.clientWidth || 0)) {
868        this.tipEL.style.transform = `translateX(${x - this.tipEL.clientWidth - 1}px)`;
869      } else {
870        this.tipEL.style.transform = `translateX(${x}px)`;
871      }
872    }
873  }
874
875  onMouseLeave(x: number, y: number) {
876    if (this.tipEL) {
877      this.tipEL.style.display = 'none';
878    }
879  }
880
881  draw(useCache: boolean = false) {
882    this.dpr = window.devicePixelRatio || 1;
883    if (this.sleeping) {
884      return;
885    }
886    if (this.online) {
887      if (!useCache && !TraceRow.isUserInteraction) {
888        this.supplier?.().then((res) => {
889          this.onThreadHandler?.(useCache, res as any);
890        });
891      }
892      this.onThreadHandler?.(useCache, null);
893      return;
894    }
895    if (!this.isComplete) {
896      if (this.supplier && !this.isLoading) {
897        this.isLoading = true;
898        this.must = true;
899        if (this.supplier) {
900          let promise = this.supplier();
901          if (promise) {
902            promise.then((res) => {
903              this.dataList = res;
904              if (this.onComplete) {
905                this.onComplete();
906              }
907              window.publish(window.SmartEvent.UI.TraceRowComplete, this);
908              this.isComplete = true;
909              this.isLoading = false;
910              this.draw(false);
911            });
912          } else {
913            this.isLoading = false;
914            this.draw(false);
915          }
916        }
917      }
918    } else {
919      if (!this.hasAttribute('row-hidden')) {
920        if (this.onThreadHandler && this.dataList) {
921          this.onThreadHandler!(false, null);
922        }
923      }
924    }
925  }
926
927  canvasSave(ctx: CanvasRenderingContext2D) {
928    ctx.save();
929    ctx.translate(0, this.translateY);
930    const clipRect = new Path2D();
931    clipRect.rect(0, 0, this.frame.width, this.frame.height);
932    ctx.clip(clipRect);
933  }
934
935  canvasRestore(ctx: CanvasRenderingContext2D) {
936    drawSelectionRange(ctx, this);
937    ctx.restore();
938  }
939
940  clearCanvas(ctx: CanvasRenderingContext2D) {
941    if (ctx) {
942      this.canvas.forEach((it) => {
943        ctx.clearRect(0, 0, it!.clientWidth || 0, it!.clientHeight || 0);
944      });
945    }
946  }
947
948  drawLines(ctx: CanvasRenderingContext2D) {
949    if (ctx) {
950      ctx.lineWidth = 1;
951      ctx.strokeStyle = this.getLineColor();
952      TraceRow.range?.xs.forEach((it) => {
953        ctx.moveTo(Math.floor(it), 0);
954        ctx.lineTo(Math.floor(it), this.shadowRoot?.host.clientHeight || 0);
955      });
956      ctx.stroke();
957    }
958  }
959
960  getLineColor() {
961    return window.getComputedStyle(this.rootEL!, null).getPropertyValue('border-bottom-color');
962  }
963
964  drawSelection(ctx: CanvasRenderingContext2D) {
965    if (this.rangeSelect) {
966      TraceRow.rangeSelectObject!.startX = Math.floor(
967        ns2x(
968          TraceRow.rangeSelectObject!.startNS!,
969          TraceRow.range!.startNS,
970          TraceRow.range!.endNS,
971          TraceRow.range!.totalNS!,
972          this.frame
973        )
974      );
975      TraceRow.rangeSelectObject!.endX = Math.floor(
976        ns2x(
977          TraceRow.rangeSelectObject!.endNS!,
978          TraceRow.range!.startNS,
979          TraceRow.range!.endNS,
980          TraceRow.range!.totalNS!,
981          this.frame
982        )
983      );
984      if (ctx) {
985        ctx.globalAlpha = 0.5;
986        ctx.fillStyle = '#666666';
987        ctx.fillRect(
988          TraceRow.rangeSelectObject!.startX!,
989          this.frame.y,
990          TraceRow.rangeSelectObject!.endX! - TraceRow.rangeSelectObject!.startX!,
991          this.frame.height
992        );
993        ctx.globalAlpha = 1;
994      }
995    }
996  }
997
998  isInTimeRange(startTime: number, duration: number): boolean {
999    return (
1000      (startTime || 0) + (duration || 0) > (TraceRow.range?.startNS || 0) &&
1001      (startTime || 0) < (TraceRow.range?.endNS || 0)
1002    );
1003  }
1004
1005  buildArgs(obj: any) {
1006    let result: any = {
1007      list: this.must ? this.dataList : undefined,
1008      offscreen: !this.isTransferCanvas ? this.offscreen[0] : undefined, //是否离屏
1009      dpr: this.dpr, //屏幕dpr值
1010      xs: TraceRow.range?.xs, //线条坐标信息
1011      isHover: this.isHover,
1012      hoverX: this.hoverX,
1013      hoverY: this.hoverY,
1014      canvasWidth: this.canvasWidth,
1015      canvasHeight: this.canvasHeight,
1016      isRangeSelect: this.rangeSelect,
1017      rangeSelectObject: TraceRow.rangeSelectObject,
1018      lineColor: this.getLineColor(),
1019      chartColor: ColorUtils.MD_PALETTE[0],
1020      startNS: TraceRow.range?.startNS || 0,
1021      endNS: TraceRow.range?.endNS || 0,
1022      totalNS: TraceRow.range?.totalNS || 0,
1023      slicesTime: TraceRow.range?.slicesTime,
1024      range: TraceRow.range,
1025      frame: this.frame,
1026      flagMoveInfo: null,
1027      flagSelectedInfo: null,
1028      wakeupBean: null,
1029    };
1030    Reflect.ownKeys(obj).forEach((it) => {
1031      result[it] = obj[it];
1032    });
1033    return result;
1034  }
1035
1036  getTransferArray() {
1037    let tsf = [];
1038    if (!this.isTransferCanvas) {
1039      tsf.push(this.offscreen[0]);
1040    }
1041    if (this.must && this.dataList instanceof ArrayBuffer) {
1042      tsf.push(this.dataList);
1043    }
1044    return tsf;
1045  }
1046
1047  attributeChangedCallback(name: string, oldValue: string, newValue: string) {
1048    switch (name) {
1049      case 'name':
1050        if (this.nameEL) {
1051          this.nameEL.textContent = newValue;
1052          this.nameEL.title = newValue;
1053        }
1054        break;
1055      case 'height':
1056        if (newValue != oldValue) {
1057          if (!this.args.isOffScreen) {
1058          }
1059        }
1060        break;
1061      case 'check-type':
1062        if (newValue === 'check') {
1063          this.checkBoxEL?.setAttribute('checked', '');
1064        } else {
1065          this.checkBoxEL?.removeAttribute('checked');
1066        }
1067        break;
1068    }
1069  }
1070
1071  focusContain(e: MouseEvent, inFavoriteArea: boolean): boolean {
1072    let _y = (e.currentTarget as HTMLElement).getBoundingClientRect().y;
1073    let myRect = this.getBoundingClientRect();
1074    let x = e.offsetX;
1075    let y = e.offsetY + _y;
1076    if (x >= myRect.x && x <= myRect.x + myRect.width && y >= myRect.y && y <= myRect.y + myRect.height) {
1077      this.hoverX = x - this.describeEl!.clientWidth;
1078      this.hoverY = y - myRect.y;
1079      this.isHover = this.collect === inFavoriteArea;
1080      return true;
1081    } else {
1082      this.isHover = false;
1083      if (this.tipEL) {
1084        this.tipEL.style.display = 'none';
1085      }
1086      return false;
1087    }
1088  }
1089
1090  initHtml(): string {
1091    return `
1092        <style>
1093        *{
1094            box-sizing: border-box;
1095        }
1096        :host(:not([row-hidden])){
1097            box-sizing: border-box;
1098            display: flex;
1099            flex-direction: column;
1100            width: 100%;
1101            height: min-content;
1102        }
1103        :host([row-hidden]){
1104            width: 100%;
1105            display: none;
1106        }
1107        .root{
1108            height: 100%;
1109            width: 100%;
1110            display: grid;
1111            grid-template-rows: 100%;
1112            grid-template-columns: 248px 1fr;
1113            border-bottom: 1px solid var(--dark-border1,#dadada);
1114            border-right: 1px solid var(--dark-border1,#ffffff);
1115            box-sizing: border-box;
1116        }
1117        .root .drag{
1118            background-color: var(--dark-background1,#eee);
1119            box-shadow: 0 4px 12px -4px #999 inset;
1120        }
1121        .root .line-top{
1122            box-shadow: 0 4px 2px -1px #4d7ab3 inset;
1123            transition: all 0.2s;
1124        }
1125        .root .line-bottom{
1126            box-shadow: 0 -4px 2px -1px #4d7ab3 inset;
1127            transition: all 0.2s;
1128        }
1129        .describe{
1130            box-sizing: border-box;
1131            border-right: 1px solid var(--dark-border1,#c9d0da);
1132            background-color: var(--dark-background5,#ffffff);
1133            align-items: center;
1134            position: relative;
1135        }
1136        .panel{
1137            width: 100%;
1138            height: 100%;
1139            overflow: visible;
1140            background-color: transparent;
1141            display: block;
1142        }
1143        .panel-container{
1144            width: 100%;
1145            position: relative;
1146            pointer-events: none;
1147        }
1148        .name{
1149            color: var(--dark-color1,#4b5766);
1150            margin-left: 10px;
1151            font-size: .9rem;
1152            font-weight: normal;
1153            flex: 1;
1154            max-height: 100%;
1155            text-align: left;
1156            overflow: hidden;
1157            user-select: none;
1158            text-overflow: ellipsis;
1159            white-space:nowrap
1160        }
1161        :host([highlight]) .name{
1162            color: #4b5766;
1163        }
1164        .icon{
1165            color: var(--dark-color1,#151515);
1166            margin-left: 10px;
1167        }
1168        .describe:hover {
1169            cursor: pointer;
1170        }
1171        :host([folder]) .describe:hover > .icon{
1172            color:#ecb93f;
1173            margin-left: 10px;
1174        }
1175        :host([folder]){
1176            /*background-color: var(--dark-background1,#f5fafb);*/
1177        }
1178        :host([folder]) .icon{
1179            display: flex;
1180        }
1181        :host(:not([folder])){
1182            /*background-color: var(--dark-background,#FFFFFF);*/
1183        }
1184        :host(:not([folder]):not([children])) {
1185        }
1186        :host(:not([folder]):not([children])) .icon{
1187            display: none;
1188        }
1189        :host(:not([folder])[children]) .icon{
1190            display: none;
1191            color:#fff
1192        }
1193
1194        :host(:not([folder])[children]) .name{
1195        }
1196        :host([expansion]) {
1197            background-color: var(--bark-expansion,#0C65D1);
1198        }
1199        :host([expansion]) .name,:host([expansion]) .icon{
1200            color: #fff;
1201        }
1202        :host([expansion]) .describe{
1203            border-right: 0px;
1204            background-color: var(--bark-expansion,#0C65D1);
1205        }
1206        :host([expansion]:not(sleeping)) .panel-container{
1207            display: none;
1208        }
1209        :host([expansion]) .children{
1210            flex-direction: column;
1211            width: 100%;
1212        }
1213        :host([expansion]) .icon{
1214            transform: rotateZ(0deg);
1215        }
1216        :host(:not([expansion])) .children{
1217            display: none;
1218            flex-direction: column;
1219            width: 100%;
1220        }
1221        :host(:not([expansion])) .icon{
1222            transform: rotateZ(-90deg);
1223        }
1224        :host([sleeping]) .describe{
1225            display: none;
1226        }
1227        :host([sleeping]) .panel-container{
1228            display: none;
1229        }
1230        :host([sleeping]) .children{
1231            display: none;
1232        }
1233        :host(:not([sleeping])) .describe{
1234            display: flex;;
1235        }
1236        :host(:not([sleeping])) .panel-container{
1237            display: block;
1238        }
1239        :host(:not([sleeping])) .children{
1240            display: flex;
1241        }
1242        :host([folder]) .lit-check-box{
1243            display: none;
1244        }
1245        :host(:not([check-type])) .lit-check-box{
1246            display: none;
1247        }
1248        :host([collect-type]) {
1249            /*position:fixed;*/
1250            /*z-index:1000;*/
1251        }
1252        :host(:not([collect-type])) {
1253            /*position:static;*/
1254        }
1255        :host([collect-type]) .collect{
1256            display: block;
1257            color: #5291FF;
1258        }
1259        :host(:not([collect-type])) .collect{
1260            display: none;
1261            color: var(--dark-icon,#666666);
1262        }
1263        .collect{
1264            margin-right: 5px;
1265        }
1266        :host(:not([folder])) .describe:hover .collect{
1267            display: block;
1268        }
1269        :host([row-type="native-memory"]) #nativeRadioList{
1270            display: flex;
1271        }
1272        .popover{
1273            color: var(--dark-color1,#4b5766);
1274            display: none;
1275            justify-content: center;
1276            align-items: center;
1277            margin-right: 5px;
1278        }
1279        .radio{
1280            margin-right: 10px;
1281
1282        }
1283        #setting{
1284            color: var(--dark-color1,#606060);
1285        }
1286        :host([expansion]) #setting{
1287            color: #FFFFFF;
1288        }
1289        :host([highlight]) .flash{
1290            background-color: #ffe263;
1291        }
1292
1293        :host([row-type="energy"]) #appNameList{
1294            display: flex;
1295        }
1296
1297         #listprocess::-webkit-scrollbar{
1298         width: 6px;
1299        }
1300
1301        /*定义滑块 内阴影+圆角*/
1302        #listprocess::-webkit-scrollbar-thumb
1303        {
1304          border-radius: 6px;
1305          background-color: var(--dark-background7,#e7c9c9);
1306        }
1307
1308        </style>
1309        <div class="root">
1310            <div class="describe flash" style="position: inherit">
1311                <lit-icon class="icon" name="caret-down" size="19"></lit-icon>
1312                <label class="name"></label>
1313                <lit-icon class="collect" name="star-fill" size="19"></lit-icon>
1314                <lit-popover placement="bottomLeft" trigger="click" id = "nativeRadioList" class="popover" haveRadio="true" style="z-index: 1;position: absolute;left: 230px">
1315                    <div style="display: block" slot="content">
1316                        <div id="first-radio" style="margin-bottom: 5px">
1317                        <input class="radio" name="status" type="radio" value="0" />Current Bytes</div>
1318                        <div id="second-radio" style="margin-bottom: 5px">
1319                        <input class="radio" name="status" type="radio" value="1" />Native Memory Density</div>
1320                    </div>
1321                    <lit-icon name="setting" size="19" id="setting"></lit-icon>
1322                </lit-popover>
1323                <lit-popover placement="bottomLeft" trigger="click" id="appNameList" class="popover" haveRadio="true" style="z-index: 1;position: absolute;left: 230px">
1324                    <div slot="content" id="listprocess" style="height:200px;overflow-y:auto">
1325                    </div>
1326                    <lit-icon name="setting" size="19" id="setting"></lit-icon>
1327                </lit-popover>
1328                <lit-check-box class="lit-check-box" style="margin-right: 10px;"></lit-check-box>
1329            </div>
1330        </div>
1331        `;
1332  }
1333}
1334