• 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 { TraceRow } from '../trace/base/TraceRow';
17import { renders } from '../../database/ui-worker/ProcedureWorker';
18import { JankRender, JankStruct } from '../../database/ui-worker/ProcedureWorkerJank';
19import { SpSystemTrace } from '../SpSystemTrace';
20import { JanksStruct } from '../../bean/JanksStruct';
21import { ns2xByTimeShaft, type PairPoint } from '../../database/ui-worker/ProcedureWorkerCommon';
22import { FrameDynamicRender, FrameDynamicStruct } from '../../database/ui-worker/ProcedureWorkerFrameDynamic';
23import { FrameAnimationRender, FrameAnimationStruct } from '../../database/ui-worker/ProcedureWorkerFrameAnimation';
24import { type BaseStruct } from '../../bean/BaseStruct';
25import { FrameSpacingRender, FrameSpacingStruct } from '../../database/ui-worker/ProcedureWorkerFrameSpacing';
26import { FlagsConfig, type Params } from '../SpFlags';
27import { type AnimationRanges, type DeviceStruct } from '../../bean/FrameComponentBean';
28import { type EmptyRender } from '../../database/ui-worker/cpu/ProcedureWorkerCPU';
29import { TreeItemData } from '../../../base-ui/tree/LitTree';
30import { QueryEnum } from '../../database/data-trafic/utils/QueryEnum';
31import { frameAnimationSender, frameDynamicSender, frameSpacingSender } from '../../database/data-trafic/FrameDynamicEffectSender';
32import { frameJanksSender } from '../../database/data-trafic/FrameJanksSender';
33import {
34  queryAnimationIdAndNameData,
35  queryAnimationTimeRangeData,
36  queryDynamicIdAndNameData,
37  queryFrameApp,
38  queryFrameTimeData, queryPhysicalData
39} from "../../database/sql/SqlLite.sql";
40import {queryAllProcessNames} from "../../database/sql/ProcessThread.sql";
41
42export class SpFrameTimeChart {
43  private trace: SpSystemTrace;
44  private flagConfig: Params | undefined;
45  private pidToProcessNameMap: Map<number, string> = new Map();
46  private idToProcessNameMap: Map<number, string> = new Map();
47
48  constructor(trace: SpSystemTrace) {
49    this.trace = trace;
50  }
51
52  async init(): Promise<void> {
53    let frameTimeData = await queryFrameTimeData();
54    this.pidToProcessNameMap.clear();
55    this.idToProcessNameMap.clear();
56    if (frameTimeData.length > 0) {
57      let processNamesArray = await queryAllProcessNames();
58      processNamesArray.forEach((it) => {
59        this.pidToProcessNameMap.set(it.pid, it.name);
60        this.idToProcessNameMap.set(it.id, it.name);
61      });
62      let frameTimeLineRow: TraceRow<JanksStruct> = await this.initFrameTimeLine();
63      await this.initExpectedChart(frameTimeLineRow);
64      await this.initActualChart(frameTimeLineRow);
65    }
66  }
67
68  async initFrameTimeLine(): Promise<TraceRow<JanksStruct>> {
69    let frameTimeLineRow: TraceRow<JanksStruct> = TraceRow.skeleton<JanksStruct>();
70    frameTimeLineRow.rowId = 'frameTime';
71    frameTimeLineRow.rowType = TraceRow.ROW_TYPE_JANK;
72    frameTimeLineRow.rowParentId = '';
73    frameTimeLineRow.style.width = '100%';
74    frameTimeLineRow.style.height = '40px';
75    frameTimeLineRow.folder = true;
76    frameTimeLineRow.name = 'FrameTimeline';
77    frameTimeLineRow.setAttribute('children', '');
78    frameTimeLineRow.supplier = (): Promise<JanksStruct[]> =>
79      new Promise((resolve) => {
80        resolve([]);
81      });
82    frameTimeLineRow.addTemplateTypes('AppStartup');
83    frameTimeLineRow.favoriteChangeHandler = this.trace.favoriteChangeHandler;
84    frameTimeLineRow.selectChangeHandler = this.trace.selectChangeHandler;
85    frameTimeLineRow.onThreadHandler = (useCache: boolean): void => {
86      let context: CanvasRenderingContext2D;
87      if (frameTimeLineRow.currentContext) {
88        context = frameTimeLineRow.currentContext;
89      } else {
90        context = frameTimeLineRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!;
91      }
92      frameTimeLineRow!.canvasSave(context);
93      (renders.jank as JankRender).renderMainThread(
94        {
95          context: context,
96          useCache: useCache,
97          type: 'expected_frame_timeline_slice',
98        },
99        frameTimeLineRow!
100      );
101      frameTimeLineRow!.canvasRestore(context, this.trace);
102    };
103    this.trace.rowsEL?.appendChild(frameTimeLineRow);
104    return frameTimeLineRow;
105  }
106
107  async initExpectedChart(frameTimeLineRow: TraceRow<JanksStruct>): Promise<void> {
108    let expectedTimeLineRow = TraceRow.skeleton<JanksStruct>();
109    expectedTimeLineRow.rowId = 'expected frameTime';
110    expectedTimeLineRow.rowType = TraceRow.ROW_TYPE_JANK;
111    expectedTimeLineRow.rowHidden = !frameTimeLineRow.expansion;
112    expectedTimeLineRow.rowParentId = 'frameTime';
113    expectedTimeLineRow.style.width = '100%';
114    expectedTimeLineRow.name = 'Expected Timeline';
115    expectedTimeLineRow.addTemplateTypes('FrameTimeline');
116    expectedTimeLineRow.setAttribute('children', '');
117    expectedTimeLineRow.supplierFrame = () => {
118      return frameJanksSender(QueryEnum.FrameExpectedData, expectedTimeLineRow).then((res) => {
119        let maxDepth: number = 1;
120        let unitHeight: number = 20;
121        res.forEach((item) => {
122          if (item.depth! >= maxDepth) {
123            maxDepth = item.depth! + 1;
124          }
125          item.frame_type = 'frameTime';
126          item.cmdline = this.pidToProcessNameMap.get(item.pid!);
127          item.rs_name = this.idToProcessNameMap.get(Number(item.rs_name)!);
128        });
129        if (expectedTimeLineRow && !expectedTimeLineRow.isComplete && res.length > 0) {
130          let maxHeight: number = maxDepth * unitHeight;
131          expectedTimeLineRow.style.height = `${maxHeight}px`;
132          expectedTimeLineRow.setAttribute('height', `${maxHeight}`);
133        }
134        return res;
135      });
136    };
137    expectedTimeLineRow.favoriteChangeHandler = this.trace.favoriteChangeHandler;
138    expectedTimeLineRow.selectChangeHandler = this.trace.selectChangeHandler;
139    expectedTimeLineRow.onThreadHandler = (useCache: boolean): void => {
140      let context: CanvasRenderingContext2D;
141      if (expectedTimeLineRow.currentContext) {
142        context = expectedTimeLineRow.currentContext;
143      } else {
144        context = expectedTimeLineRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!;
145      }
146      expectedTimeLineRow!.canvasSave(context);
147      (renders.jank as JankRender).renderMainThread(
148        {
149          context: context,
150          useCache: useCache,
151          type: 'expected_frame_timeline_slice',
152        },
153        expectedTimeLineRow!
154      );
155      expectedTimeLineRow!.canvasRestore(context, this.trace);
156    };
157    frameTimeLineRow.addChildTraceRow(expectedTimeLineRow);
158  }
159
160  async initActualChart(frameTimeLineRow: TraceRow<JanksStruct>): Promise<void> {
161    let actualTimeLineRow = TraceRow.skeleton<JanksStruct>();
162    actualTimeLineRow.rowId = 'actual frameTime';
163    actualTimeLineRow.rowType = TraceRow.ROW_TYPE_JANK;
164    actualTimeLineRow.rowHidden = !frameTimeLineRow.expansion;
165    actualTimeLineRow.rowParentId = 'frameTime';
166    actualTimeLineRow.style.width = '100%';
167    actualTimeLineRow.name = 'Actual Timeline';
168    actualTimeLineRow.addTemplateTypes('FrameTimeline');
169    actualTimeLineRow.setAttribute('children', '');
170    actualTimeLineRow.supplierFrame = () => {
171      return frameJanksSender(QueryEnum.FrameActualData, actualTimeLineRow).then((res) => {
172        let maxDepth: number = 1;
173        let unitHeight: number = 20;
174        res.forEach((item) => {
175          if (item.depth! >= maxDepth) {
176            maxDepth = item.depth! + 1;
177          }
178          item.frame_type = 'frameTime';
179          item.cmdline = this.pidToProcessNameMap.get(item.pid!);
180          item.rs_name = this.idToProcessNameMap.get(Number(item.rs_name)!);
181          item.type = '0';
182        });
183        if (actualTimeLineRow && !actualTimeLineRow.isComplete && res.length > 0) {
184          let maxHeight: number = maxDepth * unitHeight;
185          actualTimeLineRow.style.height = `${maxHeight}px`;
186          actualTimeLineRow.setAttribute('height', `${maxHeight}`);
187        }
188        return res;
189      });
190    };
191    actualTimeLineRow.favoriteChangeHandler = this.trace.favoriteChangeHandler;
192    actualTimeLineRow.selectChangeHandler = this.trace.selectChangeHandler;
193    actualTimeLineRow.onThreadHandler = (useCache: boolean): void => {
194      let context: CanvasRenderingContext2D;
195      if (actualTimeLineRow.currentContext) {
196        context = actualTimeLineRow.currentContext;
197      } else {
198        context = actualTimeLineRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!;
199      }
200      actualTimeLineRow!.canvasSave(context);
201      (renders.jank as JankRender).renderMainThread(
202        {
203          context: context,
204          useCache: useCache,
205          type: 'expected_frame_timeline_slice',
206        },
207        actualTimeLineRow!
208      );
209      actualTimeLineRow!.canvasRestore(context, this.trace);
210    };
211    frameTimeLineRow.addChildTraceRow(actualTimeLineRow);
212    let offsetYTimeOut: number = 0;
213    frameTimeLineRow.addEventListener('expansion-change', (customEventInit: CustomEventInit) => {
214      JankStruct.delJankLineFlag = false;
215      if (offsetYTimeOut) {
216        clearTimeout(offsetYTimeOut);
217      }
218      if (customEventInit.detail?.expansion) {
219        offsetYTimeOut = this.frameExpandTimeOut(customEventInit, actualTimeLineRow);
220      } else {
221        offsetYTimeOut = this.frameNoExpandTimeOut(customEventInit, frameTimeLineRow);
222      }
223    });
224  }
225
226  async initAnimatedScenesChart(
227    processRow: TraceRow<BaseStruct>,
228    process: { pid: number | null; processName: string | null },
229    firstRow: TraceRow<BaseStruct>
230  ): Promise<void> {
231    this.flagConfig = FlagsConfig.getFlagsConfig('AnimationAnalysis');
232    let appNameMap: Map<number, string> = new Map();
233    if (this.flagConfig?.AnimationAnalysis === 'Enabled') {
234      if (process.processName?.startsWith('render_service')) {
235        let targetRowList = processRow.childrenList.filter(
236          (childRow) => childRow.rowType === 'thread' && childRow.name.startsWith('render_service')
237        );
238        let nameArr: { name: string }[] = await queryFrameApp();
239        if (nameArr && nameArr.length > 0) {
240          let currentName = nameArr[0].name;
241          let frameChart = await this.initFrameChart(processRow, nameArr);
242          processRow.addChildTraceRowAfter(frameChart, targetRowList[0]);
243          let appNameList = await queryDynamicIdAndNameData();
244          appNameList.forEach((item) => {
245            appNameMap.set(item.id, item.appName);
246          });
247          let animationRanges = await this.initAnimationChart(processRow, firstRow);
248          await this.initDynamicCurveChart(appNameMap, frameChart, currentName, animationRanges);
249          await this.initFrameSpacing(appNameMap, nameArr, frameChart, currentName, animationRanges);
250        }
251      }
252    }
253  }
254
255  private async initFrameChart(
256    processRow: TraceRow<BaseStruct>,
257    nameArr: { name: string }[]
258  ): Promise<TraceRow<BaseStruct>> {
259    let frameChart: TraceRow<BaseStruct> = TraceRow.skeleton<BaseStruct>();
260    let labelName = frameChart.shadowRoot?.querySelector('.name') as HTMLLabelElement;
261    labelName.style.marginRight = '77px';
262    this.addSystemConfigButton(frameChart, nameArr, 'model-name', true);
263    frameChart.rowId = 'frame';
264    frameChart.rowType = TraceRow.ROW_TYPE_FRAME;
265    frameChart.rowHidden = !processRow.expansion;
266    frameChart.rowParentId = processRow.rowId;
267    frameChart.style.width = '100%';
268    frameChart.style.height = '40px';
269    frameChart.folder = true;
270    frameChart.name = nameArr[0].name;
271    frameChart.setAttribute('children', '');
272    frameChart.supplier = (): Promise<BaseStruct[]> =>
273      new Promise((resolve) => {
274        resolve([]);
275      });
276    frameChart.favoriteChangeHandler = this.trace.favoriteChangeHandler;
277    frameChart.selectChangeHandler = this.trace.selectChangeHandler;
278    frameChart.onThreadHandler = (useCache: boolean): void => {
279      let context: CanvasRenderingContext2D = frameChart!.collect
280        ? this.trace.canvasFavoritePanelCtx!
281        : this.trace.canvasPanelCtx!;
282      frameChart!.canvasSave(context);
283      (renders.empty as EmptyRender).renderMainThread(
284        {
285          context: context,
286          useCache: useCache,
287          type: 'frame',
288        },
289        frameChart!
290      );
291      frameChart!.canvasRestore(context, this.trace);
292    };
293    this.trace.rowsEL?.appendChild(frameChart);
294    return frameChart;
295  }
296
297  async initAnimationChart(
298    processRow: TraceRow<BaseStruct>,
299    firstRow: TraceRow<BaseStruct>
300  ): Promise<AnimationRanges[]> {
301    let animationRanges: AnimationRanges[] = [];
302    let frameAnimationRow = TraceRow.skeleton<FrameAnimationStruct>();
303    let unitIndex: number = 1;
304    let unitHeight: number = 20;
305    frameAnimationRow.rowId = 'Animation';
306    frameAnimationRow.rowType = TraceRow.ROW_TYPE_FRAME_ANIMATION;
307    frameAnimationRow.rowHidden = !processRow.expansion;
308    frameAnimationRow.rowParentId = processRow.rowId;
309    frameAnimationRow.style.width = '100%';
310    frameAnimationRow.name = 'Animation';
311    frameAnimationRow.addTemplateTypes('AnimationEffect');
312    frameAnimationRow.setAttribute('children', '');
313    let timeRangeData = await queryAnimationTimeRangeData();
314    timeRangeData.forEach((rangeTime) => {
315      if (rangeTime.status === 'Completion delay') {
316        animationRanges.push({
317          start: rangeTime.startTs,
318          end: rangeTime.endTs,
319        });
320      }
321    });
322    let animationIdNameMap: Map<number, string> = new Map<number, string>();
323    let animationIdInfoMap: Map<number, string> = new Map<number, string>();
324    let animationNameData = await queryAnimationIdAndNameData();
325    animationNameData.forEach((item) => {
326      animationIdNameMap.set(item.id, item.name);
327      animationIdInfoMap.set(item.id, item.info);
328    });
329    frameAnimationRow.supplierFrame = () => {
330      return frameAnimationSender(frameAnimationRow).then((result) => {
331        let maxDepth = 0;
332        result.forEach((item) => {
333          if (item.status == '1') {
334            item.status = 'Completion delay';
335          } else {
336            item.status = 'Response delay';
337          }
338          if (item.depth > maxDepth) {
339            maxDepth = item.depth;
340          }
341          if (animationIdNameMap.has(item.animationId!)) {
342            item.name = animationIdNameMap.get(item.animationId!);
343            item.frameInfo = item.status == 'Completion delay' ? animationIdInfoMap.get(item.animationId!) : '0';
344          }
345        });
346        let maxHeight: number = (maxDepth + unitIndex) * unitHeight;
347        frameAnimationRow.style.height = `${maxHeight}px`;
348        frameAnimationRow.setAttribute('height', `${maxHeight}`);
349        return result;
350      });
351    };
352    frameAnimationRow.favoriteChangeHandler = this.trace.favoriteChangeHandler;
353    frameAnimationRow.selectChangeHandler = this.trace.selectChangeHandler;
354    frameAnimationRow.onThreadHandler = (useCache): void => {
355      let context: CanvasRenderingContext2D = frameAnimationRow!.collect
356        ? this.trace.canvasFavoritePanelCtx!
357        : this.trace.canvasPanelCtx!;
358      frameAnimationRow!.canvasSave(context);
359      (renders.frameAnimation as FrameAnimationRender).renderMainThread(
360        {
361          context: context,
362          useCache: useCache,
363          type: 'frameAnimation',
364        },
365        frameAnimationRow!
366      );
367      frameAnimationRow!.canvasRestore(context, this.trace);
368    };
369    processRow.addChildTraceRowBefore(frameAnimationRow, firstRow);
370    return animationRanges;
371  }
372
373  async initDynamicCurveChart(
374    appNameMap: Map<number, string>,
375    frameChart: TraceRow<BaseStruct>,
376    name: string,
377    animationRanges: AnimationRanges[]
378  ): Promise<void> {
379    let systemConfigList: {
380      name: string;
381    }[] = [{ name: 'x' }, { name: 'y' }, { name: 'width' }, { name: 'height' }, { name: 'alpha' }];
382    let dynamicCurveRow: TraceRow<FrameDynamicStruct> = TraceRow.skeleton<FrameDynamicStruct>();
383    this.addSystemConfigButton(dynamicCurveRow, systemConfigList, 'model-type');
384    dynamicCurveRow.setAttribute('model-type', systemConfigList[0].name);
385    dynamicCurveRow.rowId = 'animation-Effect-Curve';
386    dynamicCurveRow.rowType = TraceRow.ROW_TYPE_FRAME_DYNAMIC;
387    dynamicCurveRow.rowHidden = !frameChart.expansion;
388    dynamicCurveRow.rowParentId = frameChart.rowId;
389    dynamicCurveRow.style.width = '100%';
390    dynamicCurveRow.style.height = '40px';
391    dynamicCurveRow.style.height = '100px';
392    let labelName = dynamicCurveRow.shadowRoot?.querySelector('.name') as HTMLLabelElement;
393    labelName.style.marginRight = '77px';
394    dynamicCurveRow.name = 'Animation Effect Curve';
395    dynamicCurveRow.addTemplateTypes('AnimationEffect');
396    dynamicCurveRow.setAttribute('height', '100px');
397    dynamicCurveRow.setAttribute('children', '');
398    dynamicCurveRow.setAttribute('model-type', systemConfigList[0].name);
399    dynamicCurveRow.setAttribute('model-name', name);
400    dynamicCurveRow.supplierFrame = () => {
401      return frameDynamicSender(dynamicCurveRow).then((result) => {
402        result.forEach((dataItem) => {
403          if (appNameMap.has(dataItem.id!)) {
404            dataItem.appName = appNameMap.get(dataItem.id!);
405          }
406        });
407        return result;
408      });
409    };
410    dynamicCurveRow.selectChangeHandler = this.trace.selectChangeHandler;
411    dynamicCurveRow.onThreadHandler = (useCache: boolean): void => {
412      let context: CanvasRenderingContext2D = dynamicCurveRow!.collect
413        ? this.trace.canvasFavoritePanelCtx!
414        : this.trace.canvasPanelCtx!;
415      dynamicCurveRow!.canvasSave(context);
416      (renders.frameDynamicCurve as FrameDynamicRender).renderMainThread(
417        {
418          context: context,
419          useCache: useCache,
420          type: 'dynamicEffectCurve',
421          animationRanges: animationRanges,
422        },
423        dynamicCurveRow!
424      );
425      dynamicCurveRow!.canvasRestore(context, this.trace);
426    };
427    frameChart.addChildTraceRow(dynamicCurveRow);
428  }
429
430  async initFrameSpacing(
431    appNameMap: Map<number, string>,
432    nameArr: { name: string }[],
433    frameChart: TraceRow<BaseStruct>,
434    name: string,
435    animationRanges: AnimationRanges[]
436  ): Promise<void> {
437    let deviceStructArray = await queryPhysicalData();
438    let deviceStruct: DeviceStruct = deviceStructArray[0];
439    let frameSpacingRow = TraceRow.skeleton<FrameSpacingStruct>();
440    frameSpacingRow.rowId = 'frame spacing';
441    frameSpacingRow.rowType = TraceRow.ROW_TYPE_FRAME_SPACING;
442    frameSpacingRow.rowHidden = !frameChart.expansion;
443    frameSpacingRow.rowParentId = frameChart.rowId;
444    frameSpacingRow.style.width = '100%';
445    frameSpacingRow.style.height = '140px';
446    frameSpacingRow.name = 'Frame spacing';
447    frameSpacingRow.addTemplateTypes('AnimationEffect');
448    frameSpacingRow.setAttribute('height', '140');
449    frameSpacingRow.setAttribute('children', '');
450    frameSpacingRow.setAttribute('model-name', name);
451    let physicalConfigWidth = Number(this.flagConfig!.physicalWidth);
452    let physicalConfigHeight = Number(this.flagConfig!.physicalHeight);
453    let physicalWidth = physicalConfigWidth !== 0 ? physicalConfigWidth : deviceStruct.physicalWidth;
454    let physicalHeight = physicalConfigHeight !== 0 ? physicalConfigHeight : deviceStruct.physicalHeight;
455    frameSpacingRow.supplierFrame = () => {
456      return frameSpacingSender(physicalWidth, physicalHeight, frameSpacingRow).then((result) => {
457        result.forEach((dataItem) => {
458          if (appNameMap.has(dataItem.id!)) {
459            dataItem.nameId = appNameMap.get(dataItem.id!);
460          }
461          dataItem.physicalWidth = physicalWidth;
462          dataItem.physicalHeight = physicalHeight;
463        });
464        return result;
465      });
466    };
467    frameSpacingRow.favoriteChangeHandler = this.trace.favoriteChangeHandler;
468    frameSpacingRow.selectChangeHandler = this.trace.selectChangeHandler;
469    frameSpacingRow.onThreadHandler = (useCache: boolean): void => {
470      let context = frameSpacingRow!.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!;
471      frameSpacingRow!.canvasSave(context);
472      (renders.frameSpacing as FrameSpacingRender).renderMainThread(
473        {
474          context: context,
475          useCache: useCache,
476          type: 'frame_spacing_slice',
477          frameRate: deviceStruct.physicalFrameRate,
478          animationRanges: animationRanges,
479        },
480        frameSpacingRow!
481      );
482      frameSpacingRow!.canvasRestore(context, this.trace);
483    };
484    frameChart.addChildTraceRow(frameSpacingRow);
485  }
486
487  addSystemConfigButton(
488    systemTraceRow: TraceRow<BaseStruct>,
489    systemConfigList: { name: string }[],
490    attributeKey: string,
491    allowChangeName: boolean = false
492  ): void {
493    let componentList: Array<TreeItemData> = [];
494    for (let index = 0; index < systemConfigList.length; index++) {
495      let componentName = systemConfigList[index].name;
496      componentList.push({
497        key: `${componentName}`,
498        title: `${componentName}`,
499        checked: index === 0,
500      });
501    }
502    systemTraceRow.addRowSettingPop();
503    systemTraceRow.rowSetting = 'enable';
504    systemTraceRow.rowSettingPopoverDirection = 'bottomLeft';
505    systemTraceRow.rowSettingList = componentList;
506    systemTraceRow.onRowSettingChangeHandler = (value: string[]): void => {
507      if (allowChangeName) {
508        systemTraceRow.name = value[0];
509      }
510      systemTraceRow.setAttribute(attributeKey, `${value[0]}`);
511      systemTraceRow.childrenList.forEach((row): void => {
512        row.setAttribute(attributeKey, `${value[0]}`);
513      });
514      this.trace.refreshCanvas(false);
515    };
516  }
517
518  private frameNoExpandTimeOut(
519    event: CustomEventInit<{
520      expansion: boolean;
521      rowType: string;
522      rowId: string;
523      rowParentId: string;
524    }>,
525    frameTimeLineRow: TraceRow<JanksStruct>
526  ): number {
527    if (JankStruct!.selectJankStruct) {
528      JankStruct.selectJankStructList?.push(<JankStruct>JankStruct!.selectJankStruct);
529    }
530    let topPadding: number = 195;
531    let halfNumber: number = 2;
532    let offsetYTime: number = 300;
533    let refreshTime: number = 360;
534    let offsetYTimeOut: number = window.setTimeout(() => {
535      this.trace.linkNodes.forEach((linkNode: PairPoint[]) => {
536        if (linkNode[0].rowEL.collect) {
537          linkNode[0].rowEL.translateY = linkNode[0].rowEL.getBoundingClientRect().top - topPadding;
538        } else {
539          linkNode[0].rowEL.translateY = linkNode[0].rowEL.offsetTop - this.trace.rowsPaneEL!.scrollTop;
540        }
541        linkNode[0].y = linkNode[0].rowEL!.translateY! + linkNode[0].offsetY;
542        if (linkNode[1].rowEL.collect) {
543          linkNode[1].rowEL.translateY = linkNode[1].rowEL.getBoundingClientRect().top - topPadding;
544        } else {
545          linkNode[1].rowEL.translateY = linkNode[1].rowEL.offsetTop - this.trace.rowsPaneEL!.scrollTop;
546        }
547        linkNode[1].y = linkNode[1].rowEL!.translateY! + linkNode[1].offsetY;
548        if (linkNode[0].rowEL.rowParentId === event.detail?.rowId) {
549          if (!linkNode[0].rowEL.collect) {
550            linkNode[0].x = ns2xByTimeShaft(linkNode[0].ns, this.trace.timerShaftEL!);
551            linkNode[0].y = frameTimeLineRow!.translateY! + linkNode[0].offsetY / halfNumber;
552            linkNode[0].offsetY = linkNode[0].offsetY / halfNumber;
553            linkNode[0].rowEL = frameTimeLineRow;
554          }
555        } else if (linkNode[1].rowEL.rowParentId === event.detail?.rowId) {
556          if (!linkNode[1].rowEL.collect) {
557            linkNode[1].x = ns2xByTimeShaft(linkNode[1].ns, this.trace.timerShaftEL!);
558            linkNode[1].y = frameTimeLineRow!.translateY! + linkNode[1].offsetY / halfNumber;
559            linkNode[1].offsetY = linkNode[1].offsetY / halfNumber;
560            linkNode[1].rowEL = frameTimeLineRow!;
561          }
562        }
563      });
564    }, offsetYTime);
565    let refreshTimeOut: number = window.setTimeout(() => {
566      this.trace.refreshCanvas(true);
567      clearTimeout(refreshTimeOut);
568    }, refreshTime);
569    return offsetYTimeOut;
570  }
571
572  private frameExpandTimeOut(
573    event: CustomEventInit<{ expansion: boolean; rowType: string; rowId: string; rowParentId: string }>,
574    actualTimeLineRow: TraceRow<JanksStruct>
575  ): number {
576    let topPadding: number = 195;
577    let halfNumber: number = 2;
578    let offsetYTime: number = 300;
579    let refreshTime: number = 360;
580    let offsetYTimeOut: number = window.setTimeout(() => {
581      this.trace.linkNodes.forEach((linkFrameNode: PairPoint[]) => {
582        JankStruct.selectJankStructList?.forEach((dat: JankStruct) => {
583          if (event.detail?.rowId === dat.pid) {
584            JankStruct.selectJankStruct = dat;
585            JankStruct.hoverJankStruct = dat;
586          }
587        });
588        if (linkFrameNode[0].rowEL.collect) {
589          linkFrameNode[0].rowEL.translateY = linkFrameNode[0].rowEL.getBoundingClientRect().top - topPadding;
590        } else {
591          linkFrameNode[0].rowEL.translateY = linkFrameNode[0].rowEL.offsetTop - this.trace.rowsPaneEL!.scrollTop;
592        }
593        linkFrameNode[0].y = linkFrameNode[0].rowEL!.translateY! + linkFrameNode[0].offsetY;
594        if (linkFrameNode[1].rowEL.collect) {
595          linkFrameNode[1].rowEL.translateY = linkFrameNode[1].rowEL.getBoundingClientRect().top - topPadding;
596        } else {
597          linkFrameNode[1].rowEL.translateY = linkFrameNode[1].rowEL.offsetTop - this.trace.rowsPaneEL!.scrollTop;
598        }
599        linkFrameNode[1].y = linkFrameNode[1].rowEL!.translateY! + linkFrameNode[1].offsetY;
600        if (linkFrameNode[0].rowEL.rowId === event.detail?.rowId) {
601          linkFrameNode[0].x = ns2xByTimeShaft(linkFrameNode[0].ns, this.trace.timerShaftEL!);
602          linkFrameNode[0].y = actualTimeLineRow!.translateY! + linkFrameNode[0].offsetY * halfNumber;
603          linkFrameNode[0].offsetY = linkFrameNode[0].offsetY * halfNumber;
604          linkFrameNode[0].rowEL = actualTimeLineRow;
605        } else if (linkFrameNode[1].rowEL.rowId === event.detail?.rowId) {
606          linkFrameNode[1].x = ns2xByTimeShaft(linkFrameNode[1].ns, this.trace.timerShaftEL!);
607          linkFrameNode[1].y = actualTimeLineRow!.translateY! + linkFrameNode[1].offsetY * halfNumber;
608          linkFrameNode[1].offsetY = linkFrameNode[1].offsetY * halfNumber;
609          linkFrameNode[1].rowEL = actualTimeLineRow!;
610        }
611      });
612    }, offsetYTime);
613    let refreshTimeOut: number = window.setTimeout(() => {
614      this.trace.refreshCanvas(true);
615      clearTimeout(refreshTimeOut);
616    }, refreshTime);
617    return offsetYTimeOut;
618  }
619}
620