• 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 '../base-ui/menu/LitMainMenu';
18import '../base-ui/icon/LitIcon';
19import { SpMetrics } from './component/SpMetrics';
20import { SpHelp } from './component/SpHelp';
21import './component/SpHelp';
22import { SpQuerySQL } from './component/SpQuerySQL';
23import './component/SpQuerySQL';
24import { SpSystemTrace } from './component/SpSystemTrace';
25import { LitMainMenu, MenuItem } from '../base-ui/menu/LitMainMenu';
26import { SpInfoAndStats } from './component/SpInfoAndStas';
27import '../base-ui/progress-bar/LitProgressBar';
28import { LitProgressBar } from '../base-ui/progress-bar/LitProgressBar';
29import { SpRecordTrace } from './component/SpRecordTrace';
30import { SpWelcomePage } from './component/SpWelcomePage';
31import { LitSearch } from './component/trace/search/Search';
32import { DbPool, threadPool } from './database/SqlLite';
33import './component/trace/search/Search';
34import './component/SpWelcomePage';
35import './component/SpSystemTrace';
36import './component/SpRecordTrace';
37import './component/SpMetrics';
38import './component/SpInfoAndStas';
39import './component/trace/base/TraceRow';
40import './component/schedulingAnalysis/SpSchedulingAnalysis';
41import { error, info, log } from '../log/Log';
42import { LitMainMenuGroup } from '../base-ui/menu/LitMainMenuGroup';
43import { LitMainMenuItem } from '../base-ui/menu/LitMainMenuItem';
44import { LitIcon } from '../base-ui/icon/LitIcon';
45import { TraceRow } from './component/trace/base/TraceRow';
46import { SpSchedulingAnalysis } from './component/schedulingAnalysis/SpSchedulingAnalysis';
47import './component/trace/base/TraceRowConfig';
48import { TraceRowConfig } from './component/trace/base/TraceRowConfig';
49import { ColorUtils } from './component/trace/base/ColorUtils';
50import { SpStatisticsHttpUtil } from '../statistics/util/SpStatisticsHttpUtil';
51import { FlagsConfig, SpFlags } from './component/SpFlags';
52import './component/SpFlags';
53import './component/trace/base/CustomThemeColor';
54import { CustomThemeColor, Theme } from './component/trace/base/CustomThemeColor';
55import { convertPool } from './database/Convert';
56import { LongTraceDBUtils } from './database/LongTraceDBUtils';
57import { type SpKeyboard } from './component/SpKeyboard';
58import './component/SpKeyboard';
59import { parseKeyPathJson } from './component/Utils';
60import { Utils } from './component/trace/base/Utils';
61import {
62  applicationHtml,
63  clearTraceFileCache,
64  findFreeSizeAlgorithm, getCurrentDataTime,
65  indexedDataToBufferData,
66  postLog,
67  readTraceFileBuffer,
68} from './SpApplicationPublicFunc';
69import { queryExistFtrace } from './database/sql/SqlLite.sql';
70
71@element('sp-application')
72export class SpApplication extends BaseElement {
73  private static loadingProgress: number = 0;
74  private static progressStep: number = 2;
75  longTraceHeadMessageList: Array<{
76    pageNum: number;
77    data: ArrayBuffer;
78  }> = [];
79
80  longTraceDataList: Array<{
81    fileType: string;
82    index: number;
83    pageNum: number;
84    startOffsetSize: number;
85    endOffsetSize: number;
86  }> = [];
87
88  longTraceTypeMessageMap:
89    | Map<
90        number,
91        Array<{
92          fileType: string;
93          startIndex: number;
94          endIndex: number;
95          size: number;
96        }>
97      >
98    | undefined
99    | null;
100  static skinChange: Function | null | undefined = null;
101  static skinChange2: Function | null | undefined = null;
102  skinChangeArray: Array<Function> = [];
103  private rootEL: HTMLDivElement | undefined | null;
104  private spWelcomePage: SpWelcomePage | undefined | null;
105  private spMetrics: SpMetrics | undefined | null;
106  private spQuerySQL: SpQuerySQL | undefined | null;
107  private spInfoAndStats: SpInfoAndStats | undefined | null;
108  private spSystemTrace: SpSystemTrace | undefined | null;
109  private spHelp: SpHelp | undefined | null;
110  private spKeyboard: SpKeyboard | undefined | null;
111  private spFlags: SpFlags | undefined | null;
112  private spRecordTrace: SpRecordTrace | undefined | null;
113  private spRecordTemplate: SpRecordTrace | undefined | null;
114  private spSchedulingAnalysis: SpSchedulingAnalysis | undefined | null;
115  private mainMenu: LitMainMenu | undefined | null;
116  private menu: HTMLDivElement | undefined | null;
117  private progressEL: LitProgressBar | undefined | null;
118  private litSearch: LitSearch | undefined | null;
119  private litRecordSearch: LitSearch | undefined | null;
120  private sidebarButton: HTMLDivElement | undefined | null;
121  private chartFilter: TraceRowConfig | undefined | null;
122  private cutTraceFile: HTMLImageElement | undefined | null;
123  private longTracePage: HTMLDivElement | undefined | null;
124  private customColor: CustomThemeColor | undefined | null;
125  private filterConfig: LitIcon | undefined | null;
126  private configClose: LitIcon | undefined | null;
127  // 关键路径标识
128  private importConfigDiv: HTMLInputElement | undefined | null;
129  private closeKeyPath: HTMLDivElement | undefined | null;
130  private importFileBt: HTMLInputElement | undefined | null;
131  private childComponent: Array<any> | undefined | null;
132  private keyCodeMap = {
133    61: true,
134    107: true,
135    109: true,
136    173: true,
137    187: true,
138    189: true,
139  };
140  private traceFileName: string | undefined;
141  colorTransiton: any;
142  static isLongTrace: boolean = false;
143  fileTypeList: string[] = ['ebpf', 'arkts', 'hiperf'];
144  private pageTimStamp: number = 0;
145  private currentPageNum: number = 1;
146  private currentDataTime: string[] = [];
147
148  static get observedAttributes(): Array<string> {
149    return ['server', 'sqlite', 'wasm', 'dark', 'vs', 'query-sql', 'subsection'];
150  }
151
152  get dark(): boolean {
153    return this.hasAttribute('dark');
154  }
155
156  set dark(value) {
157    if (value) {
158      this.rootEL!.classList.add('dark');
159      this.setAttribute('dark', '');
160    } else {
161      this.rootEL!.classList.remove('dark');
162      this.removeAttribute('dark');
163    }
164    if (this.skinChangeArray.length > 0) {
165      this.skinChangeArray.forEach((item) => item(value));
166    }
167    if (SpApplication.skinChange) {
168      SpApplication.skinChange(value);
169    }
170    if (SpApplication.skinChange2) {
171      SpApplication.skinChange2(value);
172    }
173
174    if (this.spHelp) {
175      this.spHelp.dark = value;
176    }
177  }
178
179  get sqlite(): boolean {
180    return this.hasAttribute('sqlite');
181  }
182
183  get wasm(): boolean {
184    return this.hasAttribute('wasm');
185  }
186
187  set wasm(d: any) {
188    this.setAttribute('wasm', '');
189  }
190
191  get server(): boolean {
192    return this.hasAttribute('server');
193  }
194
195  set server(s: boolean) {
196    if (s) {
197      this.setAttribute('server', '');
198    } else {
199      this.removeAttribute('server');
200    }
201  }
202
203  get querySql(): boolean {
204    return this.hasAttribute('query-sql');
205  }
206
207  set querySql(isShowMetric) {
208    if (isShowMetric) {
209      this.setAttribute('query-sql', '');
210    } else {
211      this.removeAttribute('query-sql');
212    }
213  }
214
215  set search(search: boolean) {
216    if (search) {
217      this.setAttribute('search', '');
218    } else {
219      this.removeAttribute('search');
220    }
221  }
222
223  get search(): boolean {
224    return this.hasAttribute('search');
225  }
226
227  addSkinListener(handler: Function): void {
228    this.skinChangeArray.push(handler);
229  }
230
231  removeSkinListener(handler: Function): void {
232    this.skinChangeArray.splice(this.skinChangeArray.indexOf(handler), 1);
233  }
234
235  initHtml(): string {
236    return applicationHtml;
237  }
238
239  initPlugin(): void {
240    SpStatisticsHttpUtil.initStatisticsServerConfig();
241    SpStatisticsHttpUtil.addUserVisitAction('visit');
242    LongTraceDBUtils.getInstance().createDBAndTable().then();
243  }
244
245  initElements(): void {
246    this.initPlugin();
247    this.querySql = true;
248    this.rootEL = this.shadowRoot!.querySelector<HTMLDivElement>('.root');
249    this.spWelcomePage = this.shadowRoot!.querySelector('#sp-welcome') as SpWelcomePage;
250    this.spMetrics = this.shadowRoot!.querySelector<SpMetrics>('#sp-metrics') as SpMetrics; // new SpMetrics();
251    this.spQuerySQL = this.shadowRoot!.querySelector<SpQuerySQL>('#sp-query-sql') as SpQuerySQL; // new SpQuerySQL();
252    this.spInfoAndStats = this.shadowRoot!.querySelector<SpInfoAndStats>('#sp-info-and-stats'); // new SpInfoAndStats();
253    this.spSystemTrace = this.shadowRoot!.querySelector<SpSystemTrace>('#sp-system-trace');
254    this.spHelp = this.shadowRoot!.querySelector<SpHelp>('#sp-help');
255    this.spKeyboard = this.shadowRoot!.querySelector<SpKeyboard>('#sp-keyboard') as SpKeyboard;
256    this.spFlags = this.shadowRoot!.querySelector<SpFlags>('#sp-flags') as SpFlags;
257    this.spRecordTrace = this.shadowRoot!.querySelector<SpRecordTrace>('#sp-record-trace');
258    this.spRecordTemplate = this.shadowRoot!.querySelector<SpRecordTrace>('#sp-record-template');
259    this.spSchedulingAnalysis = this.shadowRoot!.querySelector<SpSchedulingAnalysis>('#sp-scheduling-analysis');
260    this.mainMenu = this.shadowRoot?.querySelector('#main-menu') as LitMainMenu;
261    this.menu = this.mainMenu.shadowRoot?.querySelector('.menu-button') as HTMLDivElement;
262    this.progressEL = this.shadowRoot?.querySelector('.progress') as LitProgressBar;
263    this.litSearch = this.shadowRoot?.querySelector('#lit-search') as LitSearch;
264    this.litRecordSearch = this.shadowRoot?.querySelector('#lit-record-search') as LitSearch;
265    this.sidebarButton = this.shadowRoot?.querySelector('.sidebar-button');
266    this.chartFilter = this.shadowRoot?.querySelector('.chart-filter') as TraceRowConfig;
267    this.cutTraceFile = this.shadowRoot?.querySelector('.cut-trace-file') as HTMLImageElement;
268    this.longTracePage = this.shadowRoot!.querySelector('.long_trace_page') as HTMLDivElement;
269    this.customColor = this.shadowRoot?.querySelector('.custom-color') as CustomThemeColor;
270    this.filterConfig = this.shadowRoot?.querySelector('.filter-config') as LitIcon;
271    this.configClose = this.shadowRoot
272      ?.querySelector<HTMLElement>('.chart-filter')!
273      .shadowRoot?.querySelector<LitIcon>('.config-close');
274    // 关键路径标识
275    this.importConfigDiv = this.shadowRoot?.querySelector<HTMLInputElement>('#import-key-path');
276    this.closeKeyPath = this.shadowRoot?.querySelector<HTMLDivElement>('#close-key-path');
277    this.importFileBt = this.shadowRoot?.querySelector<HTMLInputElement>('#import-config');
278    this.initElementsAttr();
279    this.initEvents();
280    this.initCustomEvents();
281    this.initCustomColorHandler();
282    this.initImportConfigEvent();
283    this.initSlideMenuEvents();
284    this.initMenus();
285    this.initGlobalEvents();
286    this.initDocumentListener();
287    let urlParams = new URL(window.location.href).searchParams;
288    if (urlParams && urlParams.get('trace') && urlParams.get('link')) {
289      this.openLineFileHandler(urlParams);
290    } else {
291      this.openMenu(true);
292    }
293  }
294
295  private initElementsAttr(): void {
296    this.mainMenu!.setAttribute('main_menu', '1');
297    this.chartFilter!.setAttribute('mode', '');
298    this.chartFilter!.setAttribute('hidden', '');
299    this.customColor!.setAttribute('mode', '');
300    this.customColor!.setAttribute('hidden', '');
301    this.childComponent = [
302      this.spSystemTrace,
303      this.spRecordTrace,
304      this.spWelcomePage,
305      this.spMetrics,
306      this.spQuerySQL,
307      this.spSchedulingAnalysis,
308      this.spInfoAndStats,
309      this.spHelp,
310      this.spRecordTemplate,
311      this.spFlags,
312      this.spKeyboard,
313    ];
314  }
315
316  private openLongTraceFile(ev: any, isRecordTrace: boolean = false) {
317    this.openFileInit();
318    let detail = (ev as any).detail;
319    let initRes = this.longTraceFileInit(isRecordTrace, detail);
320    if (!isRecordTrace && initRes) {
321      let that = this;
322      let readSize = 0;
323      let timStamp = new Date().getTime();
324      const { traceTypePage, allFileSize, normalTraceNames, specialTraceNames } = initRes;
325      if (normalTraceNames.length <= 0) {
326        return;
327      }
328      const readFiles = async (
329        files: FileList,
330        traceTypePage: Array<number>,
331        normalNames: Array<string>,
332        specialNames: Array<string>
333      ): Promise<any> => {
334        const promises = Array.from(files).map((file) => {
335          if (normalNames.indexOf(file.name.toLowerCase()) >= 0) {
336            return that.longTraceFileRead(file, true, traceTypePage, readSize, timStamp, allFileSize);
337          } else if (specialNames.indexOf(file.name.toLowerCase()) >= 0) {
338            return that.longTraceFileRead(file, false, traceTypePage, readSize, timStamp, allFileSize);
339          } else {
340            return;
341          }
342        });
343        return Promise.all(promises);
344      };
345      this.litSearch!.setPercent('Read in file: ', 1);
346      readFiles(detail, traceTypePage, normalTraceNames, specialTraceNames).then(() => {
347        this.litSearch!.setPercent('Cut in file: ', 1);
348        this.sendCutFileMessage(timStamp);
349      });
350    }
351  }
352
353  private longTraceFileRead = async (
354    file: any,
355    isNormalType: boolean,
356    traceTypePage: Array<number>,
357    readSize: number,
358    timStamp: number,
359    allFileSize: number
360  ): Promise<boolean> => {
361    info('reading long trace file ', file.name);
362    let that = this;
363    return new Promise((resolve, reject) => {
364      let fr = new FileReader();
365      let message = { fileType: '', startIndex: 0, endIndex: 0, size: 0 };
366      info('Parse long trace using wasm mode ');
367      const { fileType, pageNumber } = this.getFileTypeAndPages(file.name, isNormalType, traceTypePage);
368      let chunk = 48 * 1024 * 1024;
369      let offset = 0;
370      let sliceLen = 0;
371      let index = 1;
372      fr.onload = function (): void {
373        let data = fr.result as ArrayBuffer;
374        LongTraceDBUtils.getInstance()
375          .addLongTableData(data, fileType, timStamp, pageNumber, index, offset, sliceLen)
376          .then(() => {
377            that.longTraceFileReadMessagePush(index, isNormalType, pageNumber, offset, sliceLen, fileType, data);
378            offset += sliceLen;
379            if (offset < file.size) {
380              index++;
381            }
382            continueReading();
383          });
384      };
385      function continueReading(): void {
386        if (offset >= file.size) {
387          message.endIndex = index;
388          message.size = file.size;
389          that.longTraceFileReadMessageHandler(pageNumber, message);
390          resolve(true);
391          return;
392        }
393        if (index === 1) {
394          message.fileType = fileType;
395          message.startIndex = index;
396        }
397        sliceLen = Math.min(file.size - offset, chunk);
398        let slice = file.slice(offset, offset + sliceLen);
399        readSize += slice.size;
400        let percentValue = ((readSize * 100) / allFileSize).toFixed(2);
401        that.litSearch!.setPercent('Read in file: ', Number(percentValue));
402        fr.readAsArrayBuffer(slice);
403      }
404      continueReading();
405      fr.onerror = (): void => reject(false);
406      info('read over long trace file ', file.name);
407    });
408  };
409
410  getFileTypeAndPages(fileName: string, isNormalType: boolean, traceTypePage: Array<number>): any {
411    let fileType = 'trace';
412    let pageNumber = 0;
413    let firstLastIndexOf = fileName.lastIndexOf('.');
414    let firstText = fileName.slice(0, firstLastIndexOf);
415    let resultLastIndexOf = firstText.lastIndexOf('_');
416    let searchResult = firstText.slice(resultLastIndexOf + 1, firstText.length);
417    if (isNormalType) {
418      pageNumber = traceTypePage.lastIndexOf(Number(searchResult));
419    } else {
420      fileType = searchResult;
421    }
422    return { fileType, pageNumber };
423  }
424
425  private longTraceFileInit(isRecordTrace: boolean, detail: any): any {
426    if (!this.wasm) {
427      this.progressEL!.loading = false;
428      return;
429    }
430    if (this.longTracePage) {
431      this.longTracePage.style.display = 'none';
432      this.litSearch!.style.marginLeft = '0px';
433      this.shadowRoot!.querySelector('.page-number-list')!.innerHTML = '';
434    }
435    this.currentPageNum = 1;
436    if (isRecordTrace) {
437      this.sendCutFileMessage(detail.timeStamp);
438      return undefined;
439    } else {
440      this.longTraceHeadMessageList = [];
441      this.longTraceTypeMessageMap = undefined;
442      this.longTraceDataList = [];
443      let traceTypePage: Array<number> = [];
444      let allFileSize = 0;
445      let normalTraceNames: Array<string> = [];
446      let specialTraceNames: Array<string> = [];
447      for (let index = 0; index < detail.length; index++) {
448        let file = detail[index];
449        let fileName = file.name as string;
450        allFileSize += file.size;
451        let specialMatch = fileName.match(/_(arkts|ebpf|hiperf)\.htrace$/);
452        let normalMatch = fileName.match(/_\d{8}_\d{6}_\d+\.htrace$/);
453        if (normalMatch) {
454          normalTraceNames.push(fileName);
455          let fileNameStr = fileName.split('.')[0];
456          let pageMatch = fileNameStr.match(/\d+$/);
457          if (pageMatch) {
458            traceTypePage.push(Number(pageMatch[0]));
459          }
460        } else if (specialMatch) {
461          specialTraceNames.push(fileName);
462        }
463      }
464      if (normalTraceNames.length <= 0) {
465        this.traceFileLoadFailedHandler('No large trace files exists in the folder!');
466      }
467      traceTypePage.sort((leftNum: number, rightNum: number) => leftNum - rightNum);
468      return { traceTypePage, allFileSize, normalTraceNames, specialTraceNames };
469    }
470  }
471
472  longTraceFileReadMessagePush(
473    index: number,
474    isNormalType: boolean,
475    pageNumber: number,
476    offset: number,
477    sliceLen: number,
478    fileType: string,
479    data: ArrayBuffer
480  ) {
481    if (index === 1 && isNormalType) {
482      this.longTraceHeadMessageList.push({
483        pageNum: pageNumber,
484        data: data.slice(offset, 1024),
485      });
486    }
487    this.longTraceDataList.push({
488      index: index,
489      fileType: fileType,
490      pageNum: pageNumber,
491      startOffsetSize: offset,
492      endOffsetSize: offset + sliceLen,
493    });
494  }
495
496  longTraceFileReadMessageHandler(pageNumber: number, message: any): void {
497    if (this.longTraceTypeMessageMap) {
498      if (this.longTraceTypeMessageMap?.has(pageNumber)) {
499        let oldTypeList = this.longTraceTypeMessageMap?.get(pageNumber);
500        oldTypeList?.push(message);
501        this.longTraceTypeMessageMap?.set(pageNumber, oldTypeList!);
502      } else {
503        this.longTraceTypeMessageMap?.set(pageNumber, [message]);
504      }
505    } else {
506      this.longTraceTypeMessageMap = new Map();
507      this.longTraceTypeMessageMap.set(pageNumber, [message]);
508    }
509  }
510
511  private openTraceFile(ev: any, isClickHandle?: boolean) {
512    this.removeAttribute('custom-color');
513    this.customColor!.setAttribute('hidden', '');
514    this.longTracePage!.style.display = 'none';
515    this.litSearch!.style.marginLeft = '0px';
516    let pageListDiv = this.shadowRoot?.querySelector('.page-number-list') as HTMLDivElement;
517    pageListDiv.innerHTML = '';
518    this.openFileInit();
519    if (this.importConfigDiv && this.closeKeyPath) {
520      this.importConfigDiv.style.display = 'none';
521      this.closeKeyPath.style.display = 'none';
522    }
523    let fileName = (ev as any).name;
524    this.traceFileName = fileName;
525    let showFileName = fileName.lastIndexOf('.') === -1 ? fileName : fileName.substring(0, fileName.lastIndexOf('.'));
526    TraceRow.rangeSelectObject = undefined;
527    if (this.sqlite) {
528      info('Parse trace using sql mode');
529      this.handleSqliteMode(ev, showFileName, (ev as any).size, fileName);
530    }
531    if (this.wasm) {
532      info('Parse trace using wasm mode ');
533      this.handleWasmMode(ev, showFileName, (ev as any).size, fileName);
534    }
535  }
536
537  private openLineFileHandler(urlParams: URLSearchParams): void {
538    this.openFileInit();
539    this.openMenu(false);
540    let downloadLineFile = urlParams.get('local') ? false : true;
541    this.setProgress(downloadLineFile ? 'download trace file' : 'open trace file');
542    this.downloadOnLineFile(
543      urlParams.get('trace') as string,
544      downloadLineFile,
545      (arrayBuf, fileName, showFileName, fileSize) => {
546        this.handleWasmMode(new File([arrayBuf], fileName), showFileName, fileSize, fileName);
547      },
548      (localPath) => {
549        let path = urlParams.get('trace') as string;
550        let fileName: string = '';
551        let showFileName: string = '';
552        if (urlParams.get('local')) {
553          this.openMenu(true);
554          fileName = urlParams.get('traceName') as string;
555        } else {
556          fileName = path.split('/').reverse()[0];
557        }
558        this.traceFileName = fileName;
559        showFileName = fileName.lastIndexOf('.') === -1 ? fileName : fileName.substring(0, fileName.lastIndexOf('.'));
560        TraceRow.rangeSelectObject = undefined;
561        let localUrl = downloadLineFile ? `${window.location.origin}${localPath}` : urlParams.get('trace')!;
562        fetch(localUrl)
563          .then((res) => {
564            res.arrayBuffer().then((arrayBuf) => {
565              if (urlParams.get('local')) {
566                URL.revokeObjectURL(localUrl);
567              }
568              this.handleWasmMode(new File([arrayBuf], fileName), showFileName, arrayBuf.byteLength, fileName);
569            });
570          })
571          .catch((e) => {
572            if (!downloadLineFile) {
573              const firstQuestionMarkIndex = window.location.href.indexOf('?');
574              location.replace(window.location.href.substring(0, firstQuestionMarkIndex));
575            }
576          });
577      }
578    );
579  }
580
581  private openMenu(open: boolean): void {
582    if (this.mainMenu) {
583      this.mainMenu.style.width = open ? '248px' : '0px';
584      this.mainMenu.style.zIndex = open ? '2000' : '0';
585    }
586    if (this.sidebarButton) {
587      this.sidebarButton.style.width = open ? '0px' : '48px';
588      this.importConfigDiv!.style.left = open ? '5px' : '45px';
589      this.closeKeyPath!.style.left = open ? '25px' : '65px';
590    }
591  }
592
593  private initGlobalDropEvents(): void {
594    let body = document.querySelector('body');
595    body!.addEventListener(
596      'drop',
597      (e: any) => {
598        e.preventDefault();
599        e.stopPropagation();
600        if (this.rootEL!.classList.contains('filedrag')) {
601          this.rootEL!.classList.remove('filedrag');
602        }
603        if (e.dataTransfer.items !== undefined && e.dataTransfer.items.length > 0) {
604          let item = e.dataTransfer.items[0];
605          if (item.webkitGetAsEntry()?.isFile) {
606            this.openTraceFile(item.getAsFile());
607          } else if (item.webkitGetAsEntry()?.isDirectory) {
608            this.litSearch!.setPercent('This File is not supported!', -1);
609            this.progressEL!.loading = false;
610            this.freshMenuDisable(false);
611            this.mainMenu!.menus!.splice(1, 1);
612            this.mainMenu!.menus = this.mainMenu!.menus!;
613            this.spSystemTrace!.reset(null);
614          }
615        }
616      },
617      false
618    );
619  }
620  private initGlobalEvents(): void {
621    let body = document.querySelector('body');
622    body!.addEventListener(
623      'dragover',
624      (e: any) => {
625        e.preventDefault();
626        e.stopPropagation();
627        if (e.dataTransfer.items.length > 0 && e.dataTransfer.items[0].kind === 'file') {
628          e.dataTransfer.dropEffect = 'copy';
629          if (!this.rootEL!.classList.contains('filedrag')) {
630            this.rootEL!.classList.add('filedrag');
631          }
632        }
633      },
634      false
635    );
636    body!.addEventListener(
637      'dragleave',
638      (e) => {
639        e.stopPropagation();
640        e.preventDefault();
641        if (this.rootEL!.classList.contains('filedrag')) {
642          this.rootEL!.classList.remove('filedrag');
643        }
644      },
645      false
646    );
647    this.initGlobalDropEvents();
648  }
649
650  private initDocumentListener(): void {
651    document.addEventListener('visibilitychange', () => {
652      if (document.visibilityState === 'visible') {
653        this.validateFileCacheLost();
654        if (window.localStorage.getItem('Theme') === 'dark') {
655          this.changeTheme(Theme.DARK);
656        } else {
657          this.changeTheme(Theme.LIGHT);
658        }
659      }
660    });
661    document.addEventListener('keydown', (event) => {
662      const e = event || window.event;
663      const ctrlKey = e.ctrlKey || e.metaKey;
664      if (ctrlKey && (this.keyCodeMap as any)[e.keyCode]) {
665        e.preventDefault();
666      } else if (e.detail) {
667        // Firefox
668        event.returnValue = false;
669      }
670    });
671    document.body.addEventListener(
672      'wheel',
673      (e) => {
674        if (e.ctrlKey) {
675          if (e.deltaY < 0) {
676            e.preventDefault();
677            return false;
678          }
679          if (e.deltaY > 0) {
680            e.preventDefault();
681            return false;
682          }
683        }
684      },
685      { passive: false }
686    );
687  }
688
689  private initMenus(): void {
690    this.mainMenu!.menus = [
691      {
692        collapsed: false,
693        title: 'Navigation',
694        second: false,
695        icon: '',
696        describe: 'Open or record a new trace',
697        children: [
698          {
699            title: 'Open trace file',
700            icon: 'folder',
701            fileChoose: true,
702            fileHandler: (ev: InputEvent): void => {
703              this.openTraceFile(ev.detail as any);
704            },
705            clickHandler: (hand: any) => {
706              this.openTraceFile(hand, true);
707            },
708          },
709          {
710            title: 'Open long trace file',
711            icon: 'folder',
712            fileChoose: true,
713            fileHandler: (ev: InputEvent): void => {
714              this.openLongTraceFile(ev);
715            },
716            clickHandler: (hand: any): void => {
717              this.openLongTraceFile(hand, true);
718            },
719          },
720          {
721            title: 'Record new trace',
722            icon: 'copyhovered',
723            clickHandler: (item: MenuItem): void => {
724              this.spRecordTrace!.synchronizeDeviceList();
725              this.spRecordTemplate!.record_template = false;
726              this.spRecordTrace!.refreshConfig(true);
727              this.showContent(this.spRecordTrace!);
728            },
729          },
730          {
731            title: 'Record template',
732            icon: 'copyhovered',
733            clickHandler: (item: MenuItem): void => {
734              this.spRecordTemplate!.refreshHint();
735              this.spRecordTemplate!.record_template = true;
736              this.spRecordTemplate!.refreshConfig(false);
737              this.spRecordTemplate!.synchronizeDeviceList();
738              this.showContent(this.spRecordTemplate!);
739            },
740          },
741        ],
742      },
743      {
744        collapsed: false,
745        title: 'Support',
746        second: false,
747        icon: '',
748        describe: 'Support',
749        children: [
750          {
751            title: 'Help Documents',
752            icon: 'smart-help',
753            clickHandler: (item: MenuItem): void => {
754              this.spHelp!.dark = this.dark;
755              this.search = false;
756              this.showContent(this.spHelp!);
757              SpStatisticsHttpUtil.addOrdinaryVisitAction({
758                event: 'help_page',
759                action: 'help_doc',
760              });
761            },
762          },
763          {
764            title: 'Flags',
765            icon: 'menu',
766            clickHandler: (item: MenuItem): void => {
767              this.search = false;
768              this.showContent(this.spFlags!);
769              SpStatisticsHttpUtil.addOrdinaryVisitAction({
770                event: 'flags',
771                action: 'flags',
772              });
773            },
774          },
775          {
776            title: 'Keyboard shortcuts',
777            icon: 'smart-help',
778            clickHandler: (item: MenuItem): void => {
779              this.search = false;
780              this.showContent(this.spKeyboard!);
781              SpStatisticsHttpUtil.addOrdinaryVisitAction({
782                event: 'Keyboard shortcuts',
783                action: 'Keyboard shortcuts',
784              });
785            },
786          },
787        ],
788      },
789    ];
790  }
791
792  private handleSqliteMode(ev: any, showFileName: string, fileSize: number, fileName: string): void {
793    let that = this;
794    let fileSizeStr = (fileSize / 1048576).toFixed(1);
795    postLog(fileName, fileSizeStr);
796    document.title = `${showFileName} (${fileSizeStr}M)`;
797    this.litSearch!.setPercent('', 0);
798    threadPool.init('sqlite').then((res) => {
799      let reader = new FileReader();
800      reader.readAsArrayBuffer(ev as any);
801      reader.onloadend = function (ev): void {
802        SpApplication.loadingProgress = 0;
803        SpApplication.progressStep = 3;
804        that.spSystemTrace!.loadDatabaseArrayBuffer(
805          this.result as ArrayBuffer,
806          '',
807          (command: string, _: number) => {
808            that.setProgress(command);
809          },
810          () => {
811            that.mainMenu!.menus!.splice(1, that.mainMenu!.menus!.length > 2 ? 1 : 0, {
812              collapsed: false,
813              title: 'Current Trace',
814              second: false,
815              icon: '',
816              describe: 'Actions on the current trace',
817              children: that.getTraceOptionMenus(showFileName, fileSizeStr, fileName, false),
818            });
819            that.litSearch!.setPercent('', 101);
820            that.chartFilter!.setAttribute('mode', '');
821            that.progressEL!.loading = false;
822            that.freshMenuDisable(false);
823          }
824        );
825      };
826    });
827  }
828
829  private handleWasmMode(ev: any, showFileName: string, fileSize: number, fileName: string): void {
830    let that = this;
831    let fileSizeStr = (fileSize / 1048576).toFixed(1);
832    postLog(fileName, fileSizeStr);
833    document.title = `${showFileName} (${fileSizeStr}M)`;
834    info('Parse trace using wasm mode ');
835    this.litSearch!.setPercent('', 1);
836    let completeHandler = async (res: any): Promise<void> => {
837      await this.traceLoadCompleteHandler(res, fileSizeStr, showFileName, fileName);
838    };
839    threadPool.init('wasm').then((res) => {
840      let reader: FileReader | null = new FileReader();
841      reader.readAsArrayBuffer(ev as any);
842      reader.onloadend = function (ev): void {
843        info('read file onloadend');
844        that.litSearch!.setPercent('ArrayBuffer loaded  ', 2);
845        let wasmUrl = `https://${window.location.host.split(':')[0]}:${window.location.port}/application/wasm.json`;
846        SpApplication.loadingProgress = 0;
847        SpApplication.progressStep = 3;
848        let data = this.result as ArrayBuffer;
849        info('initData start Parse Data');
850        that.spSystemTrace!.loadDatabaseArrayBuffer(
851          data,
852          wasmUrl,
853          (command: string, _: number) => that.setProgress(command),
854          completeHandler
855        );
856      };
857    });
858  }
859
860  private async traceLoadCompleteHandler(
861    res: any,
862    fileSize: string,
863    showFileName: string,
864    fileName: string
865  ): Promise<void> {
866    let existFtrace = await queryExistFtrace();
867    let isAllowTrace = true;
868    if (DbPool.sharedBuffer) {
869      let traceHeadData = new Uint8Array(DbPool.sharedBuffer!.slice(0, 10));
870      let enc = new TextDecoder();
871      let headerStr = enc.decode(traceHeadData);
872      let rowTraceStr = Array.from(new Uint8Array(DbPool.sharedBuffer!.slice(0, 2)))
873        .map((byte) => byte.toString(16).padStart(2, '0'))
874        .join('');
875      if (headerStr.indexOf('OHOSPROF') !== 0 && rowTraceStr.indexOf('49df') !== 0) {
876        isAllowTrace = false;
877      }
878      this.cutTraceFile!.style.display = 'block';
879      DbPool.sharedBuffer = null;
880    }
881    let index = 2;
882    if (existFtrace.length > 0 && isAllowTrace) {
883      this.showConvertTraceMenu(fileName);
884      index = 3;
885    }
886    this.loadTraceCompleteMenuHandler(index);
887    if (res.status) {
888      info('loadDatabaseArrayBuffer success');
889      this.showCurrentTraceMenu(fileSize, showFileName, fileName);
890      this.importConfigDiv!.style.display = Utils.SCHED_SLICE_MAP.size > 0 ? 'block' : 'none';
891      this.showContent(this.spSystemTrace!);
892      this.litSearch!.setPercent('', 101);
893      this.chartFilter!.setAttribute('mode', '');
894      this.freshMenuDisable(false);
895    } else {
896      info('loadDatabaseArrayBuffer failed');
897      this.litSearch!.setPercent(res.msg || 'This File is not supported!', -1);
898      this.freshMenuDisable(false);
899      this.mainMenu!.menus!.splice(1, 1);
900      this.mainMenu!.menus = this.mainMenu!.menus!;
901    }
902    this.progressEL!.loading = false;
903    this.spInfoAndStats!.initInfoAndStatsData();
904  }
905
906  private showConvertTraceMenu(fileName: string): void {
907    this.mainMenu!.menus!.splice(2, 1, {
908      collapsed: false,
909      title: 'Convert trace',
910      second: false,
911      icon: '',
912      describe: 'Convert to other formats',
913      children: this.pushConvertTrace(fileName),
914    });
915  }
916
917  private showCurrentTraceMenu(fileSize: string, showFileName: string, fileName: string): void {
918    this.mainMenu!.menus!.splice(1, this.mainMenu!.menus!.length > 2 ? 1 : 0, {
919      collapsed: false,
920      title: 'Current Trace',
921      second: false,
922      icon: '',
923      describe: 'Actions on the current trace',
924      children: this.getTraceOptionMenus(showFileName, fileSize, fileName, false),
925    });
926  }
927
928  private loadTraceCompleteMenuHandler(index: number): void {
929    let that = this;
930    this.mainMenu!.menus!.splice(index, 1, {
931      collapsed: false,
932      title: 'Support',
933      second: false,
934      icon: '',
935      describe: 'Support',
936      children: [
937        {
938          title: 'Help Documents',
939          icon: 'smart-help',
940          clickHandler: function (item: MenuItem) {
941            SpStatisticsHttpUtil.addOrdinaryVisitAction({
942              event: 'help_page',
943              action: 'help_doc',
944            });
945            that.search = false;
946            that.spHelp!.dark = that.dark;
947            that.showContent(that.spHelp!);
948          },
949        },
950        {
951          title: 'Flags',
952          icon: 'menu',
953          clickHandler: function (item: MenuItem): void {
954            SpStatisticsHttpUtil.addOrdinaryVisitAction({
955              event: 'flags',
956              action: 'flags',
957            });
958            that.search = false;
959            that.showContent(that.spFlags!);
960          },
961        },
962        {
963          title: 'Keyboard shortcuts',
964          icon: 'smart-help',
965          clickHandler: function (item: MenuItem): void {
966            SpStatisticsHttpUtil.addOrdinaryVisitAction({
967              event: 'Keyboard shortcuts',
968              action: 'Keyboard shortcuts',
969            });
970            that.search = false;
971            that.showContent(that.spKeyboard!);
972          },
973        },
974      ],
975    });
976  }
977
978  private validateGetTraceFileByPage(): boolean {
979    if (!this.wasm) {
980      this.progressEL!.loading = false;
981      return false;
982    }
983    return this.pageTimStamp !== 0;
984  }
985
986  private queryFileByPage(
987    instance: LongTraceDBUtils,
988    indexedDbPageNum: number,
989    maxTraceFileLength: number,
990    traceRange: IDBKeyRange
991  ) {
992    instance.indexedDBHelp.get(instance.tableName, traceRange, 'QueryFileByPage').then((result) => {
993      let traceData = indexedDataToBufferData(result);
994      let ebpfRange = this.getIDBKeyRange(indexedDbPageNum, 'ebpf_new');
995      let arkTsRange = this.getIDBKeyRange(indexedDbPageNum, 'arkts_new');
996      let hiperfRange = this.getIDBKeyRange(indexedDbPageNum, 'hiperf_new');
997      Promise.all([
998        instance.getByRange(ebpfRange),
999        instance.getByRange(arkTsRange),
1000        instance.getByRange(hiperfRange),
1001      ]).then((otherResult) => {
1002        let ebpfData = indexedDataToBufferData(otherResult[0]);
1003        let arkTsData = indexedDataToBufferData(otherResult[1]);
1004        let hiperfData = indexedDataToBufferData(otherResult[2]);
1005        let traceArray = new Uint8Array(traceData);
1006        let ebpfArray = new Uint8Array(ebpfData);
1007        let arkTsArray = new Uint8Array(arkTsData);
1008        let hiPerfArray = new Uint8Array(hiperfData);
1009        let allOtherData = [ebpfData, arkTsData, hiperfData];
1010        let otherDataLength = traceData.byteLength + ebpfData.byteLength + arkTsData.byteLength + hiperfData.byteLength;
1011        let timeStamp = this.currentDataTime[0] + this.currentDataTime[1] + this.currentDataTime[2] + '_' +
1012          this.currentDataTime[3] + this.currentDataTime[4] + this.currentDataTime[5];
1013        this.traceFileName = `hiprofiler_long_${timeStamp}_${indexedDbPageNum}.htrace`;
1014        if (otherDataLength > maxTraceFileLength) {
1015          if (traceData.byteLength > maxTraceFileLength) {
1016            this.traceFileLoadFailedHandler('hitrace file too big!');
1017          } else {
1018            let freeDataLength = maxTraceFileLength - traceData.byteLength;
1019            let freeDataIndex = findFreeSizeAlgorithm(
1020              [ebpfData.byteLength, arkTsData.byteLength, hiperfData.byteLength],
1021              freeDataLength
1022            );
1023            let finalData = [traceData];
1024            freeDataIndex.forEach((dataIndex) => {
1025              finalData.push(allOtherData[dataIndex]);
1026            });
1027            const file = new File([new Blob(finalData)], this.traceFileName);
1028            this.handleWasmMode(file, file.name, file.size, this.traceFileName);
1029          }
1030        } else {
1031          let fileBlob = new Blob([traceArray, ebpfArray, arkTsArray, hiPerfArray]);
1032          const file = new File([fileBlob], this.traceFileName);
1033          this.handleWasmMode(file, file.name, file.size, file.name);
1034        }
1035      });
1036    });
1037  }
1038
1039  private getTraceFileByPage(pageNumber: number): void {
1040    this.openFileInit();
1041    if (this.validateGetTraceFileByPage()) {
1042      let indexedDbPageNum = pageNumber - 1;
1043      let maxTraceFileLength = 400 * 1024 * 1024;
1044      let traceRange = this.getIDBKeyRange(indexedDbPageNum, 'trace');
1045      let instance = LongTraceDBUtils.getInstance();
1046      this.queryFileByPage(instance, indexedDbPageNum, maxTraceFileLength, traceRange);
1047    }
1048  }
1049
1050  private traceFileLoadFailedHandler(reason: string): void {
1051    this.litSearch!.isLoading = false;
1052    this.litSearch!.setPercent(reason, -1);
1053    this.progressEL!.loading = false;
1054    this.freshMenuDisable(false);
1055  }
1056
1057  private getIDBKeyRange(indexedDbPageNum: number, key: string): IDBKeyRange {
1058    return IDBKeyRange.bound(
1059      [this.pageTimStamp, key, indexedDbPageNum],
1060      [this.pageTimStamp, key, indexedDbPageNum],
1061      false,
1062      false
1063    );
1064  }
1065
1066  private refreshPageListHandler(
1067    pageListDiv: HTMLDivElement,
1068    previewButton: HTMLDivElement,
1069    nextButton: HTMLDivElement,
1070    pageInput: HTMLInputElement
1071  ): void {
1072    this.progressEL!.loading = true;
1073    this.refreshPageList(
1074      pageListDiv,
1075      previewButton!,
1076      nextButton!,
1077      pageInput!,
1078      this.currentPageNum,
1079      this.longTraceHeadMessageList.length
1080    );
1081    this.getTraceFileByPage(this.currentPageNum);
1082  }
1083
1084  private sendCutFileMessage(timStamp: number): void {
1085    this.pageTimStamp = timStamp;
1086    threadPool.init('wasm').then(() => {
1087      let headUintArray = new Uint8Array(this.longTraceHeadMessageList.length * 1024);
1088      let headOffset = 0;
1089      this.longTraceHeadMessageList = this.longTraceHeadMessageList.sort(
1090        (leftMessage, rightMessage) => leftMessage.pageNum - rightMessage.pageNum
1091      );
1092      for (let index = 0; index < this.longTraceHeadMessageList.length; index++) {
1093        let currentUintArray = new Uint8Array(this.longTraceHeadMessageList[index].data);
1094        headUintArray.set(currentUintArray, headOffset);
1095        headOffset += currentUintArray.length;
1096      }
1097      threadPool.submit(
1098        'ts-cut-file',
1099        '',
1100        {
1101          headArray: headUintArray,
1102          timeStamp: timStamp,
1103          splitFileInfo: this.longTraceTypeMessageMap?.get(0),
1104          splitDataList: this.longTraceDataList,
1105        },
1106        (res: Array<any>) => {
1107          this.litSearch!.setPercent('Cut in file ', 100);
1108          this.currentDataTime = getCurrentDataTime();
1109          if (this.longTraceHeadMessageList.length > 0) {
1110            this.getTraceFileByPage(this.currentPageNum);
1111            this.litSearch!.style.marginLeft = '80px';
1112            this.longTracePage!.style.display = 'flex';
1113            this.initCutFileEvent();
1114          } else {
1115            this.progressEL!.loading = false;
1116            this.litSearch!.setPercent('Missing basic trace in the large-file scenario!', -1);
1117            this.freshMenuDisable(false);
1118            return;
1119          }
1120        },
1121        'long_trace'
1122      );
1123    });
1124  }
1125
1126  private initCutFileEvent(): void {
1127    let pageListDiv = this.shadowRoot?.querySelector('.page-number-list') as HTMLDivElement;
1128    let previewButton: HTMLDivElement | null | undefined =
1129      this.shadowRoot?.querySelector<HTMLDivElement>('#preview-button');
1130    let nextButton: HTMLDivElement | null | undefined = this.shadowRoot?.querySelector<HTMLDivElement>('#next-button');
1131    let pageInput = this.shadowRoot?.querySelector<HTMLInputElement>('.page-input');
1132    pageListDiv.innerHTML = '';
1133    this.refreshPageList(
1134      pageListDiv,
1135      previewButton!,
1136      nextButton!,
1137      pageInput!,
1138      this.currentPageNum,
1139      this.longTraceHeadMessageList.length
1140    );
1141    this.initCutFileNextOrPreEvents(previewButton!, nextButton!, pageListDiv, pageInput!);
1142    let nodeListOf = pageListDiv.querySelectorAll<HTMLDivElement>('div');
1143    nodeListOf.forEach((divEL, index) => {
1144      divEL.addEventListener('click', () => {
1145        if (this.progressEL!.loading) {
1146          return;
1147        }
1148        if (divEL.textContent === '...') {
1149          let freeSize = Number(nodeListOf[index + 1].textContent) - Number(nodeListOf[index - 1].textContent);
1150          this.currentPageNum = Math.floor(freeSize / 2 + Number(nodeListOf[index - 1].textContent));
1151        } else {
1152          this.currentPageNum = Number(divEL.textContent);
1153        }
1154        this.refreshPageListHandler(pageListDiv, previewButton!, nextButton!, pageInput!);
1155      });
1156    });
1157    pageInput!.addEventListener('input', () => {
1158      let value = pageInput!.value;
1159      value = value.replace(/\D/g, '');
1160      if (value) {
1161        value = Math.min(this.longTraceHeadMessageList.length, parseInt(value)).toString();
1162      }
1163      pageInput!.value = value;
1164    });
1165    let pageConfirmEl = this.shadowRoot?.querySelector<HTMLDivElement>('.confirm-button');
1166    pageConfirmEl!.addEventListener('click', () => {
1167      if (this.progressEL!.loading) {
1168        return;
1169      }
1170      this.currentPageNum = Number(pageInput!.value);
1171      this.refreshPageListHandler(pageListDiv, previewButton!, nextButton!, pageInput!);
1172    });
1173  }
1174
1175  private initCutFileNextOrPreEvents(
1176    previewButton: HTMLDivElement,
1177    nextButton: HTMLDivElement,
1178    pageListDiv: HTMLDivElement,
1179    pageInput: HTMLInputElement
1180  ): void {
1181    if (previewButton) {
1182      previewButton.addEventListener('click', () => {
1183        if (this.progressEL!.loading || this.currentPageNum === 1) {
1184          return;
1185        }
1186        if (this.currentPageNum > 1) {
1187          this.currentPageNum--;
1188          this.refreshPageListHandler(pageListDiv, previewButton!, nextButton!, pageInput!);
1189        }
1190      });
1191    }
1192    nextButton!.addEventListener('click', () => {
1193      if (this.progressEL!.loading || this.currentPageNum === this.longTraceHeadMessageList.length) {
1194        return;
1195      }
1196      if (this.currentPageNum < this.longTraceHeadMessageList.length) {
1197        this.currentPageNum++;
1198        this.refreshPageListHandler(pageListDiv, previewButton!, nextButton!, pageInput!);
1199      }
1200    });
1201  }
1202
1203  private initCustomColorHandler(): void {
1204    let customColorShow = this.shadowRoot
1205      ?.querySelector('lit-main-menu')!
1206      .shadowRoot!.querySelector('.customColor') as HTMLDivElement;
1207    customColorShow.addEventListener('click', (ev) => {
1208      if (this!.hasAttribute('custom-color')) {
1209        this!.removeAttribute('custom-color');
1210        this.customColor!.setAttribute('hidden', '');
1211        this.customColor!.cancelOperate();
1212      } else {
1213        this!.removeAttribute('chart_filter');
1214        this.chartFilter!.setAttribute('hidden', '');
1215        this!.setAttribute('custom-color', '');
1216        this.customColor!.removeAttribute('hidden');
1217      }
1218    });
1219  }
1220
1221  private openFileInit(): void {
1222    clearTraceFileCache();
1223    this.litSearch!.clear();
1224    SpStatisticsHttpUtil.addOrdinaryVisitAction({
1225      event: 'open_trace',
1226      action: 'open_trace',
1227    });
1228    info('openTraceFile');
1229    this.spSystemTrace!.clearPointPair();
1230    this.spSystemTrace!.reset((command: string, percent: number) => {
1231      this.setProgress(command);
1232    });
1233    window.publish(window.SmartEvent.UI.MouseEventEnable, {
1234      mouseEnable: false,
1235    });
1236    window.clearTraceRowComplete();
1237    this.freshMenuDisable(true);
1238    SpSchedulingAnalysis.resetCpu();
1239    if (this.mainMenu!.menus!.length > 3) {
1240      this.mainMenu!.menus!.splice(1, 2);
1241      this.mainMenu!.menus = this.mainMenu!.menus!;
1242    } else if (this.mainMenu!.menus!.length > 2) {
1243      this.mainMenu!.menus!.splice(1, 1);
1244      this.mainMenu!.menus = this.mainMenu!.menus!;
1245    }
1246    this.showContent(this.spSystemTrace!);
1247    this.progressEL!.loading = true;
1248  }
1249
1250  private restoreDownLoadIcons() {
1251    let querySelectorAll = this.mainMenu!.shadowRoot?.querySelectorAll<LitMainMenuGroup>('lit-main-menu-group');
1252    querySelectorAll!.forEach((menuGroup) => {
1253      let attribute = menuGroup.getAttribute('title');
1254      if (attribute === 'Convert trace') {
1255        let querySelectors = menuGroup.querySelectorAll<LitMainMenuItem>('lit-main-menu-item');
1256        querySelectors.forEach((item) => {
1257          if (item.getAttribute('title') === 'Convert to .systrace') {
1258            item!.setAttribute('icon', 'download');
1259            let querySelector = item!.shadowRoot?.querySelector('.icon') as LitIcon;
1260            querySelector.removeAttribute('spin');
1261          }
1262        });
1263      }
1264    });
1265  }
1266
1267  private postConvert(fileName: string): void {
1268    let newFileName = fileName.substring(0, fileName.lastIndexOf('.')) + '.systrace';
1269    let aElement = document.createElement('a');
1270    convertPool.submitWithName('getConvertData', (status: boolean, msg: string, results: Blob) => {
1271      aElement.href = URL.createObjectURL(results);
1272      aElement.download = newFileName;
1273      let timeoutId = 0;
1274      aElement.addEventListener('click', (ev) => {
1275        clearTimeout(timeoutId);
1276        timeoutId = window.setTimeout(() => {
1277          this.restoreDownLoadIcons();
1278        }, 2000);
1279      });
1280      aElement.click();
1281      window.URL.revokeObjectURL(aElement.href);
1282    });
1283  }
1284
1285  private pushConvertTrace(fileName: string): Array<any> {
1286    let instance = this;
1287    let menus = [];
1288    menus.push({
1289      title: 'Convert to .systrace',
1290      icon: 'download',
1291      clickHandler: function () {
1292        convertPool.init('convert').then((item) => {
1293          let querySelectorAll =
1294            instance.mainMenu!.shadowRoot?.querySelectorAll<LitMainMenuGroup>('lit-main-menu-group');
1295          querySelectorAll!.forEach((menuGroup) => {
1296            let attribute = menuGroup.getAttribute('title');
1297            if (attribute === 'Convert trace') {
1298              let querySelectors = menuGroup.querySelectorAll<LitMainMenuItem>('lit-main-menu-item');
1299              querySelectors.forEach((item) => {
1300                if (item.getAttribute('title') === 'Convert to .systrace') {
1301                  item!.setAttribute('icon', 'convert-loading');
1302                  item!.classList.add('pending');
1303                  item!.style.fontKerning = '';
1304                  let querySelector = item!.shadowRoot?.querySelector('.icon') as LitIcon;
1305                  querySelector.setAttribute('spin', '');
1306                }
1307              });
1308            }
1309          });
1310          instance.postConvert(fileName);
1311        });
1312      },
1313    });
1314    return menus;
1315  }
1316
1317  private setProgress(command: string): void {
1318    if (command === 'database ready' && SpApplication.loadingProgress < 50) {
1319      SpApplication.progressStep = 6;
1320    }
1321    if (command === 'process' && SpApplication.loadingProgress < 92) {
1322      SpApplication.loadingProgress = 92 + Math.round(Math.random() * SpApplication.progressStep);
1323    } else {
1324      SpApplication.loadingProgress += Math.round(Math.random() * SpApplication.progressStep + Math.random());
1325    }
1326    if (SpApplication.loadingProgress > 99) {
1327      SpApplication.loadingProgress = 99;
1328    }
1329    info('setPercent :' + command + 'percent :' + SpApplication.loadingProgress);
1330    this.litSearch!.setPercent(command + '  ', SpApplication.loadingProgress);
1331  }
1332
1333  private getTraceOptionMenus(
1334    showFileName: string,
1335    fileSize: string,
1336    fileName: string,
1337    isServer: boolean,
1338    dbName?: string
1339  ): Array<any> {
1340    let menus = [
1341      {
1342        title: `${showFileName} (${fileSize}M)`,
1343        icon: 'file-fill',
1344        clickHandler: (): void => {
1345          this.search = true;
1346          this.showContent(this.spSystemTrace!);
1347        },
1348      },
1349      {
1350        title: 'Scheduling Analysis',
1351        icon: 'piechart-circle-fil',
1352        clickHandler: (): void => {
1353          SpStatisticsHttpUtil.addOrdinaryVisitAction({
1354            event: 'Scheduling Analysis',
1355            action: 'scheduling_analysis',
1356          });
1357          this.showContent(this.spSchedulingAnalysis!);
1358          this.spSchedulingAnalysis!.init();
1359        },
1360      },
1361      {
1362        title: 'Download File',
1363        icon: 'download',
1364        clickHandler: (): void => {
1365          this.download(this.mainMenu!, fileName, isServer, dbName);
1366          SpStatisticsHttpUtil.addOrdinaryVisitAction({
1367            event: 'download',
1368            action: 'download',
1369          });
1370        },
1371      },
1372      {
1373        title: 'Download Database',
1374        icon: 'download',
1375        clickHandler: (): void => {
1376          this.downloadDB(this.mainMenu!, fileName);
1377          SpStatisticsHttpUtil.addOrdinaryVisitAction({
1378            event: 'download_db',
1379            action: 'download',
1380          });
1381        },
1382      },
1383    ];
1384    this.getTraceQuerySqlMenus(menus);
1385    if ((window as any).cpuCount === 0 || !FlagsConfig.getFlagsConfigEnableStatus('SchedulingAnalysis')) {
1386      menus.splice(1, 1);
1387    }
1388    return menus;
1389  }
1390
1391  private getTraceQuerySqlMenus(menus: Array<any>): void {
1392    if (this.querySql) {
1393      if (this.spQuerySQL) {
1394        this.spQuerySQL!.reset();
1395        menus.push({
1396          title: 'Query (SQL)',
1397          icon: 'filesearch',
1398          clickHandler: () => {
1399            this.showContent(this.spQuerySQL!);
1400          },
1401        });
1402      }
1403      if (this.spMetrics) {
1404        this.spMetrics!.reset();
1405        menus.push({
1406          title: 'Metrics',
1407          icon: 'metric',
1408          clickHandler: () => {
1409            this.showContent(this.spMetrics!);
1410          },
1411        });
1412      }
1413      if (this.spInfoAndStats) {
1414        menus.push({
1415          title: 'Info and stats',
1416          icon: 'info',
1417          clickHandler: () => {
1418            SpStatisticsHttpUtil.addOrdinaryVisitAction({
1419              event: 'info',
1420              action: 'info_stats',
1421            });
1422            this.showContent(this.spInfoAndStats!);
1423          },
1424        });
1425      }
1426    }
1427  }
1428
1429  private initSlideMenuEvents(): void {
1430    //打开侧边栏
1431    this.sidebarButton!.onclick = (e): void => {
1432      if (this.mainMenu) {
1433        this.mainMenu.style.width = '248px';
1434        this.mainMenu.style.zIndex = '2000';
1435        this.mainMenu.style.display = 'flex';
1436      }
1437      if (this.sidebarButton) {
1438        this.sidebarButton.style.width = '0px';
1439        this.importConfigDiv!.style.left = '5px';
1440        this.closeKeyPath!.style.left = '25px';
1441      }
1442    };
1443    let icon: HTMLDivElement | undefined | null = this.mainMenu?.shadowRoot?.querySelector('div.header > div');
1444    icon!.style.pointerEvents = 'none';
1445    icon!.onclick = (e): void => {
1446      if (this.mainMenu) {
1447        this.mainMenu.style.width = '0px';
1448        this.mainMenu.style.display = 'flex';
1449        this.mainMenu.style.zIndex = '0';
1450      }
1451      if (this.sidebarButton) {
1452        this.sidebarButton.style.width = '48px';
1453        this.importConfigDiv!.style.left = '45px';
1454        this.closeKeyPath!.style.left = '65px';
1455      }
1456    };
1457  }
1458
1459  private initImportConfigEvent(): void {
1460    this.importFileBt?.addEventListener('change', (): void => {
1461      let files = this.importFileBt!.files;
1462      if (files && files.length === 1) {
1463        const reader = new FileReader();
1464        reader.readAsText(files[0], 'UTF-8');
1465        reader.onload = (e): void => {
1466          if (e.target?.result) {
1467            try {
1468              const result = parseKeyPathJson(e.target.result as string);
1469              window.publish(window.SmartEvent.UI.KeyPath, result);
1470              this.closeKeyPath!.style.display = 'block';
1471            } catch {
1472              error('json Parse Failed');
1473              this.litSearch!.setPercent('Json Parse Failed!', -1);
1474              window.setTimeout(() => {
1475                this.litSearch!.setPercent('Json Parse Failed!', 101);
1476              }, 1000);
1477            }
1478          } else {
1479            window.publish(window.SmartEvent.UI.KeyPath, []);
1480            this.closeKeyPath!.style.display = 'none';
1481          }
1482        };
1483      }
1484      this.importFileBt!.files = null;
1485      this.importFileBt!.value = '';
1486    });
1487    if (this.closeKeyPath) {
1488      this.closeKeyPath.addEventListener('click', (): void => {
1489        window.publish(window.SmartEvent.UI.KeyPath, []);
1490        this.closeKeyPath!.style.display = 'none';
1491      });
1492    }
1493  }
1494
1495  private initCustomEvents(): void {
1496    window.subscribe(window.SmartEvent.UI.MenuTrace, () => this.showContent(this.spSystemTrace!));
1497    window.subscribe(window.SmartEvent.UI.Error, (err) => {
1498      this.litSearch!.setPercent(err, -1);
1499      this.progressEL!.loading = false;
1500      this.freshMenuDisable(false);
1501    });
1502    window.subscribe(window.SmartEvent.UI.Loading, (arg: { loading: boolean; text?: string }) => {
1503      if (arg.text) {
1504        this.litSearch!.setPercent(arg.text || '', arg.loading ? -1 : 101);
1505      }
1506      window.publish(window.SmartEvent.UI.MouseEventEnable, {
1507        mouseEnable: !arg.loading,
1508      });
1509      this.progressEL!.loading = arg.loading;
1510    });
1511  }
1512
1513  private initEvents(): void {
1514    this.addEventListener('copy', function (event) {
1515      let clipdata = event.clipboardData;
1516      let value = clipdata!.getData('text/plain');
1517      let searchValue = value.toString().trim();
1518      clipdata!.setData('text/plain', searchValue);
1519    });
1520    this.initSearchEvents();
1521    this.initSystemTraceEvents();
1522    this.filterConfig!.addEventListener('click', (ev) => {
1523      if (this!.hasAttribute('chart_filter')) {
1524        this!.removeAttribute('chart_filter');
1525        this.chartFilter!.setAttribute('hidden', '');
1526      } else {
1527        this!.removeAttribute('custom-color');
1528        this.customColor!.setAttribute('hidden', '');
1529        this.customColor!.cancelOperate();
1530        this!.setAttribute('chart_filter', '');
1531        this.chartFilter!.removeAttribute('hidden');
1532      }
1533    });
1534    this.configClose!.addEventListener('click', (ev) => {
1535      if (this.hasAttribute('chart_filter')) {
1536        this!.removeAttribute('chart_filter');
1537      }
1538    });
1539    this.cutTraceFile!.addEventListener('click', () => {
1540      this.croppingFile(this.progressEL!, this.litSearch!);
1541    });
1542  }
1543
1544  private initSearchChangeEvents(): void {
1545    this.litSearch!.valueChangeHandler = (value: string): void => {
1546      this.litSearch!.isClearValue = false;
1547      if (value.length > 0) {
1548        let list: any[] = [];
1549        this.progressEL!.loading = true;
1550        this.spSystemTrace!.searchCPU(value).then((cpus) => {
1551          list = cpus;
1552          this.spSystemTrace!.searchFunction(list, value).then((mixedResults) => {
1553            if (this.litSearch!.searchValue !== '') {
1554              this.litSearch!.list = this.spSystemTrace!.searchSdk(mixedResults, value);
1555              this.litSearch!.index = this.spSystemTrace!.showStruct(false, -1, this.litSearch!.list);
1556            }
1557            this.progressEL!.loading = false;
1558          });
1559        });
1560      } else {
1561        let indexEL = this.litSearch!.shadowRoot!.querySelector<HTMLSpanElement>('#index');
1562        indexEL!.textContent = '0';
1563        this.litSearch!.list = [];
1564        this.spSystemTrace?.visibleRows.forEach((it) => {
1565          it.highlight = false;
1566          it.draw();
1567        });
1568        this.spSystemTrace?.timerShaftEL?.removeTriangle('inverted');
1569      }
1570    };
1571  }
1572  private initSearchEvents(): void {
1573    this.litSearch!.addEventListener('focus', () => {
1574      window.publish(window.SmartEvent.UI.KeyboardEnable, {
1575        enable: false,
1576      });
1577    });
1578    this.litSearch!.addEventListener('blur', () => {
1579      window.publish(window.SmartEvent.UI.KeyboardEnable, {
1580        enable: true,
1581      });
1582    });
1583    this.litSearch!.addEventListener('previous-data', (ev: any) => {
1584      this.litSearch!.index = this.spSystemTrace!.showStruct(true, this.litSearch!.index, this.litSearch!.list);
1585      this.litSearch!.blur();
1586    });
1587    this.litSearch!.addEventListener('next-data', (ev: any) => {
1588      this.litSearch!.index = this.spSystemTrace!.showStruct(false, this.litSearch!.index, this.litSearch!.list);
1589      this.litSearch!.blur();
1590    });
1591    // 翻页事件
1592    this.litSearch!.addEventListener('retarget-data', (ev: any) => {
1593      this.litSearch!.index = this.spSystemTrace!.showStruct(
1594        true,
1595        ev.detail.value,
1596        this.litSearch!.list,
1597        ev.detail.value
1598      );
1599      this.litSearch!.blur();
1600    });
1601    this.initSearchChangeEvents();
1602  }
1603
1604  private initSystemTraceEvents(): void {
1605    this.spSystemTrace?.addEventListener('trace-previous-data', (ev: any) => {
1606      this.litSearch!.index = this.spSystemTrace!.showStruct(true, this.litSearch!.index, this.litSearch!.list);
1607    });
1608    this.spSystemTrace?.addEventListener('trace-next-data', (ev: any) => {
1609      this.litSearch!.index = this.spSystemTrace!.showStruct(false, this.litSearch!.index, this.litSearch!.list);
1610    });
1611  }
1612
1613  private showContent(showNode: HTMLElement): void {
1614    if (showNode === this.spSystemTrace) {
1615      this.menu!.style.pointerEvents = 'auto';
1616      this.sidebarButton!.style.pointerEvents = 'auto';
1617      this.search = true;
1618      this.litRecordSearch!.style.display = 'none';
1619      this.litSearch!.style.display = 'block';
1620      window.publish(window.SmartEvent.UI.KeyboardEnable, {
1621        enable: true,
1622      });
1623      this.filterConfig!.style.visibility = 'visible';
1624    } else {
1625      this.removeAttribute('custom-color');
1626      this.customColor!.setAttribute('hidden', '');
1627      this.customColor!.cancelOperate();
1628      this.menu!.style.pointerEvents = 'none';
1629      this.sidebarButton!.style.pointerEvents = 'none';
1630      this.search = this.litSearch!.isLoading;
1631      if (!this.search) {
1632        this.litSearch!.style.display = 'none';
1633        this.litRecordSearch!.style.display = 'block';
1634      }
1635      window.publish(window.SmartEvent.UI.KeyboardEnable, {
1636        enable: false,
1637      });
1638      this.filterConfig!.style.visibility = 'hidden';
1639    }
1640    this.childComponent!.forEach((node) => {
1641      if (this.hasAttribute('chart_filter')) {
1642        this.removeAttribute('chart_filter');
1643      }
1644      if (this.hasAttribute('custom-color')) {
1645        this.removeAttribute('custom-color');
1646        this.customColor!.setAttribute('hidden', '');
1647        this.customColor!.cancelOperate();
1648      }
1649      if (node === showNode) {
1650        showNode.style.visibility = 'visible';
1651      } else {
1652        (node! as HTMLElement).style.visibility = 'hidden';
1653      }
1654    });
1655  }
1656
1657  private validateFileCacheLost(): void {
1658    caches.has(DbPool.fileCacheKey).then((exist) => {
1659      if (!exist) {
1660        this.mainMenu!.menus?.forEach((mg) => {
1661          mg.children.forEach((mi: any) => {
1662            if (mi.title === 'Download File') {
1663              mi.disabled = true;
1664            }
1665          });
1666        });
1667        this.cutTraceFile!.style.display = 'none';
1668        this.mainMenu!.menus = this.mainMenu!.menus;
1669      }
1670    });
1671  }
1672
1673  private refreshPageList(
1674    pageListDiv: HTMLDivElement,
1675    previewButton: HTMLDivElement,
1676    nextButton: HTMLDivElement,
1677    pageInput: HTMLInputElement,
1678    currentPageNum: number,
1679    maxPageNumber: number
1680  ): void {
1681    if (pageInput) {
1682      pageInput.textContent = currentPageNum.toString();
1683      pageInput.value = currentPageNum.toString();
1684    }
1685    let pageText: string[] = [];
1686    if (maxPageNumber > 7) {
1687      pageText = this.largePageHandler(currentPageNum, maxPageNumber, previewButton, nextButton);
1688    } else {
1689      pageText = [];
1690      for (let index = 0; index < maxPageNumber; index++) {
1691        pageText.push((index + 1).toString());
1692      }
1693    }
1694    this.pageNodeHandler(pageListDiv, pageText, currentPageNum);
1695    nextButton.style.pointerEvents = 'auto';
1696    nextButton.style.opacity = '1';
1697    previewButton.style.pointerEvents = 'auto';
1698    previewButton.style.opacity = '1';
1699    if (currentPageNum === 1) {
1700      previewButton.style.pointerEvents = 'none';
1701      previewButton.style.opacity = '0.7';
1702    } else if (currentPageNum === maxPageNumber) {
1703      nextButton.style.pointerEvents = 'none';
1704      nextButton.style.opacity = '0.7';
1705    }
1706  }
1707
1708  private pageNodeHandler(pageListDiv: HTMLDivElement, pageText: Array<string>, currentPageNum: number): void {
1709    let pageNodeList = pageListDiv.querySelectorAll<HTMLDivElement>('div');
1710    if (pageNodeList.length > 0) {
1711      pageNodeList.forEach((page, index) => {
1712        page.textContent = pageText[index].toString();
1713        page.title = pageText[index];
1714        if (currentPageNum.toString() === pageText[index]) {
1715          page.setAttribute('selected', '');
1716        } else {
1717          if (page.hasAttribute('selected')) {
1718            page.removeAttribute('selected');
1719          }
1720        }
1721      });
1722    } else {
1723      pageListDiv.innerHTML = '';
1724      pageText.forEach((page) => {
1725        let element = document.createElement('div');
1726        element.className = 'page-number pagination';
1727        element.textContent = page.toString();
1728        element.title = page.toString();
1729        if (currentPageNum.toString() === page.toString()) {
1730          element.setAttribute('selected', '');
1731        }
1732        pageListDiv.appendChild(element);
1733      });
1734    }
1735  }
1736
1737  private largePageHandler(
1738    currentPageNum: number,
1739    maxPageNumber: number,
1740    previewButton: HTMLDivElement,
1741    nextButton: HTMLDivElement
1742  ): Array<string> {
1743    switch (currentPageNum) {
1744      case 1:
1745      case 2:
1746      case 3:
1747      case 4:
1748      case 5:
1749        return ['1', '2', '3', '4', '5', '...', maxPageNumber.toString()];
1750      case maxPageNumber:
1751      case maxPageNumber - 1:
1752      case maxPageNumber - 2:
1753      case maxPageNumber - 3:
1754      case maxPageNumber - 4:
1755        return [
1756          '1',
1757          '...',
1758          (maxPageNumber - 4).toString(),
1759          (maxPageNumber - 3).toString(),
1760          (maxPageNumber - 2).toString(),
1761          (maxPageNumber - 1).toString(),
1762          maxPageNumber.toString(),
1763        ];
1764      default:
1765        nextButton.style.pointerEvents = 'auto';
1766        previewButton!.style.pointerEvents = 'auto';
1767        nextButton.style.opacity = '1';
1768        previewButton!.style.opacity = '1';
1769        return [
1770          '1',
1771          '...',
1772          (currentPageNum - 1).toString(),
1773          currentPageNum.toString(),
1774          (currentPageNum + 1).toString(),
1775          '...',
1776          maxPageNumber.toString(),
1777        ];
1778    }
1779  }
1780
1781  /**
1782   * 修改颜色或者主题,重新绘制侧边栏和泳道图
1783   * @param theme 当前主题(深色和浅色)
1784   * @param colorsArray 预览的情况下传入
1785   */
1786  changeTheme(theme: Theme, colorsArray?: Array<string>): void {
1787    if (!colorsArray) {
1788      this.customColor!.setRadioChecked(theme);
1789    }
1790    if (theme === Theme.DARK) {
1791      this.changeDarkTheme(colorsArray);
1792    } else {
1793      this.changeLightTheme(colorsArray);
1794    }
1795    this.spSystemTrace!.timerShaftEL!.rangeRuler!.draw();
1796    if (this.colorTransiton) {
1797      clearTimeout(this.colorTransiton);
1798    }
1799    this.colorTransiton = setTimeout(() => (this.mainMenu!.style.transition = '0s'), 1000);
1800  }
1801
1802  private changeDarkTheme(colorsArray?: Array<string>): void {
1803    let menuGroup = this.mainMenu!.shadowRoot?.querySelectorAll<LitMainMenuGroup>('lit-main-menu-group');
1804    let menuItem = this.mainMenu!.shadowRoot?.querySelectorAll<LitMainMenuItem>('lit-main-menu-item');
1805    this.mainMenu!.style.backgroundColor = '#262f3c';
1806    this.mainMenu!.style.transition = '1s';
1807    menuGroup!.forEach((item) => {
1808      let groupName = item!.shadowRoot!.querySelector('.group-name') as LitMainMenuGroup;
1809      let groupDescribe = item!.shadowRoot!.querySelector('.group-describe') as LitMainMenuGroup;
1810      groupName.style.color = 'white';
1811      groupDescribe.style.color = 'white';
1812    });
1813    menuItem!.forEach((item) => {
1814      item.style.color = 'white';
1815    });
1816    if (
1817      !colorsArray &&
1818      window.localStorage.getItem('DarkThemeColors') &&
1819      ColorUtils.FUNC_COLOR_B !== JSON.parse(window.localStorage.getItem('DarkThemeColors')!)
1820    ) {
1821      ColorUtils.MD_PALETTE = JSON.parse(window.localStorage.getItem('DarkThemeColors')!);
1822      ColorUtils.FUNC_COLOR = JSON.parse(window.localStorage.getItem('DarkThemeColors')!);
1823    } else if (colorsArray) {
1824      ColorUtils.MD_PALETTE = colorsArray;
1825      ColorUtils.FUNC_COLOR = colorsArray;
1826    } else {
1827      ColorUtils.MD_PALETTE = ColorUtils.FUNC_COLOR_B;
1828      ColorUtils.FUNC_COLOR = ColorUtils.FUNC_COLOR_B;
1829    }
1830  }
1831
1832  private changeLightTheme(colorsArray?: Array<string>): void {
1833    let menuGroup = this.mainMenu!.shadowRoot?.querySelectorAll<LitMainMenuGroup>('lit-main-menu-group');
1834    let menuItem = this.mainMenu!.shadowRoot?.querySelectorAll<LitMainMenuItem>('lit-main-menu-item');
1835    this.mainMenu!.style.backgroundColor = 'white';
1836    this.mainMenu!.style.transition = '1s';
1837    menuGroup!.forEach((item) => {
1838      let groupName = item!.shadowRoot!.querySelector('.group-name') as LitMainMenuGroup;
1839      let groupDescribe = item!.shadowRoot!.querySelector('.group-describe') as LitMainMenuGroup;
1840      groupName.style.color = 'black';
1841      groupDescribe.style.color = '#92959b';
1842    });
1843    menuItem!.forEach((item) => {
1844      item.style.color = 'black';
1845    });
1846    if (
1847      !colorsArray &&
1848      window.localStorage.getItem('LightThemeColors') &&
1849      ColorUtils.FUNC_COLOR_A !== JSON.parse(window.localStorage.getItem('LightThemeColors')!)
1850    ) {
1851      ColorUtils.MD_PALETTE = JSON.parse(window.localStorage.getItem('LightThemeColors')!);
1852      ColorUtils.FUNC_COLOR = JSON.parse(window.localStorage.getItem('LightThemeColors')!);
1853    } else if (colorsArray) {
1854      ColorUtils.MD_PALETTE = colorsArray;
1855      ColorUtils.FUNC_COLOR = colorsArray;
1856    } else {
1857      ColorUtils.MD_PALETTE = ColorUtils.FUNC_COLOR_A;
1858      ColorUtils.FUNC_COLOR = ColorUtils.FUNC_COLOR_A;
1859    }
1860  }
1861
1862  private downloadOnLineFile(
1863    url: string,
1864    download: boolean,
1865    openUrl: (buffer: ArrayBuffer, fileName: string, showFileName: string, fileSize: number) => void,
1866    openFileHandler: (path: string) => void
1867  ): void {
1868    if (download) {
1869      fetch(url)
1870        .then((res) => {
1871          res.arrayBuffer().then((arrayBuf) => {
1872            let fileName = url.split('/').reverse()[0];
1873            let showFileName =
1874              fileName.lastIndexOf('.') === -1 ? fileName : fileName.substring(0, fileName.lastIndexOf('.'));
1875            openUrl(arrayBuf, fileName, showFileName, arrayBuf.byteLength);
1876          });
1877        })
1878        .catch((e) => {
1879          let api = `${window.location.origin}/application/download-file`;
1880          fetch(api, {
1881            method: 'POST',
1882            headers: {
1883              'Content-Type': 'application/x-www-form-urlencoded',
1884            },
1885            body: new URLSearchParams({
1886              url: url,
1887            }),
1888          })
1889            .then((response) => response.json())
1890            .then((res) => {
1891              if (res.code === 0 && res.success) {
1892                let resultUrl = res.data.url;
1893                if (resultUrl) {
1894                  openFileHandler(resultUrl.toString().replace(/\\/g, '/'));
1895                }
1896              }
1897            });
1898        });
1899    } else {
1900      openFileHandler(url);
1901    }
1902  }
1903
1904  private croppingFile(progressEL: LitProgressBar, litSearch: LitSearch): void {
1905    let cutLeftNs = TraceRow.rangeSelectObject?.startNS || 0;
1906    let cutRightNs = TraceRow.rangeSelectObject?.endNS || 0;
1907    if (cutRightNs === cutLeftNs) {
1908      return;
1909    }
1910    let recordStartNS = (window as any).recordStartNS;
1911    let cutLeftTs = recordStartNS + cutLeftNs;
1912    let cutRightTs = recordStartNS + cutRightNs;
1913    let minCutDur = 1_000_000;
1914    if (cutRightTs - cutLeftTs < minCutDur) {
1915      let unitTs = (cutRightTs - cutLeftTs) / 2;
1916      let midTs = cutLeftTs + unitTs;
1917      cutLeftTs = midTs - minCutDur / 2;
1918      cutRightTs = midTs + minCutDur / 2;
1919    }
1920    progressEL.loading = true;
1921    threadPool.cutFile(cutLeftTs, cutRightTs, (status: boolean, msg: string, cutBuffer?: ArrayBuffer) => {
1922      progressEL.loading = false;
1923      if (status) {
1924        FlagsConfig.updateFlagsConfig('FfrtConvert', 'Disabled');
1925        let traceFileName = this.traceFileName as string;
1926        let cutIndex = traceFileName.indexOf('_cut_');
1927        let fileType = traceFileName.substring(traceFileName.lastIndexOf('.'));
1928        let traceName = document.title.replace(/\s*\([^)]*\)/g, '').trim();
1929        if (cutIndex !== -1) {
1930          traceName = traceName.substring(0, cutIndex);
1931        }
1932        let blobUrl = URL.createObjectURL(new Blob([cutBuffer!]));
1933        window.open(
1934          `index.html?link=true&local=true&traceName=${traceName}_cut_${cutLeftTs}${fileType}&trace=${encodeURIComponent(
1935            blobUrl
1936          )}`
1937        );
1938      } else {
1939        litSearch.setPercent(msg, -1);
1940        window.setTimeout(() => {
1941          litSearch.setPercent(msg, 101);
1942        }, 1000);
1943      }
1944    });
1945  }
1946
1947  private downloadDB(mainMenu: LitMainMenu, fileDbName: string): void {
1948    let fileName = `${fileDbName?.substring(0, fileDbName?.lastIndexOf('.'))}.db`;
1949    threadPool.submit(
1950      'download-db',
1951      '',
1952      {},
1953      (reqBufferDB: any) => {
1954        let a = document.createElement('a');
1955        a.href = URL.createObjectURL(new Blob([reqBufferDB]));
1956        a.download = fileName;
1957        a.click();
1958        this.itemIconLoading(mainMenu, 'Current Trace', 'Download Database', true);
1959        let that = this;
1960        let timer = setInterval(function () {
1961          that.itemIconLoading(mainMenu, 'Current Trace', 'Download Database', false);
1962          clearInterval(timer);
1963        }, 4000);
1964      },
1965      'download-db'
1966    );
1967  }
1968
1969  private async download(mainMenu: LitMainMenu, fileName: string, isServer: boolean, dbName?: string): Promise<void> {
1970    let a = document.createElement('a');
1971    if (isServer) {
1972      if (dbName !== '') {
1973        let file = dbName?.substring(0, dbName?.lastIndexOf('.')) + fileName.substring(fileName.lastIndexOf('.'));
1974        a.href = `https://${window.location.host.split(':')[0]}:${window.location.port}${file}`;
1975      } else {
1976        return;
1977      }
1978    } else {
1979      let buffer = await readTraceFileBuffer();
1980      if (buffer) {
1981        a.href = URL.createObjectURL(new Blob([buffer]));
1982      }
1983    }
1984    a.download = fileName;
1985    a.click();
1986    window.URL.revokeObjectURL(a.href);
1987    let that = this;
1988    this.itemIconLoading(mainMenu, 'Current Trace', 'Download File', true);
1989    let timer = setInterval(function () {
1990      that.itemIconLoading(mainMenu, 'Current Trace', 'Download File', false);
1991      clearInterval(timer);
1992    }, 4000);
1993  }
1994
1995  private itemIconLoading(mainMenu: LitMainMenu, groupName: string, itemName: string, start: boolean): void {
1996    let currentTraceGroup = mainMenu.shadowRoot?.querySelector<LitMainMenuGroup>(
1997      `lit-main-menu-group[title='${groupName}']`
1998    );
1999    let downloadItem = currentTraceGroup!.querySelector<LitMainMenuItem>(`lit-main-menu-item[title='${itemName}']`);
2000    let downloadIcon = downloadItem!.shadowRoot?.querySelector('.icon') as LitIcon;
2001    if (start) {
2002      downloadItem!.setAttribute('icon', 'convert-loading');
2003      downloadIcon.setAttribute('spin', '');
2004    } else {
2005      downloadItem!.setAttribute('icon', 'download');
2006      downloadIcon.removeAttribute('spin');
2007    }
2008  }
2009
2010  freshMenuDisable(disable: boolean): void {
2011    // @ts-ignore
2012    this.mainMenu!.menus[0].children[0].disabled = disable;
2013    // @ts-ignore
2014    this.mainMenu!.menus[0].children[1].disabled = disable;
2015    if (this.mainMenu!.menus!.length > 2) {
2016      // @ts-ignore
2017      this.mainMenu!.menus[1].children.map((it) => (it.disabled = disable));
2018    }
2019    this.mainMenu!.menus = this.mainMenu!.menus;
2020    this.filterConfig!.style.visibility = disable ? 'hidden' : 'visible';
2021  }
2022}
2023