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.js"; 17import "../base-ui/menu/LitMainMenu.js"; 18import "../base-ui/icon/LitIcon.js"; 19import {SpSystemTrace} from "./component/SpSystemTrace.js"; 20import {LitMainMenu, MenuItem} from "../base-ui/menu/LitMainMenu.js"; 21import "../base-ui/progress-bar/LitProgressBar.js"; 22import {LitProgressBar} from "../base-ui/progress-bar/LitProgressBar.js"; 23import {SpRecordTrace} from "./component/SpRecordTrace.js"; 24import {SpWelcomePage} from "./component/SpWelcomePage.js"; 25import {SpSearch} from "./component/trace/search/Search.js"; 26import {threadPool} from "./database/SqlLite.js"; 27import "./component/trace/search/Search.js"; 28import "./component/SpWelcomePage.js"; 29import "./component/SpSystemTrace.js"; 30import "./component/SpWelcomePage.js"; 31import "./component/SpRecordTrace.js"; 32import {TraceRow} from "./component/trace/base/TraceRow.js"; 33 34@element('sp-application') 35export class SpApplication extends BaseElement { 36 static skinChange: Function | null | undefined = null; 37 static skinChange2: Function | null | undefined = null; 38 skinChangeArray: Array<Function> = []; 39 private rootEL: HTMLDivElement | undefined | null 40 41 static get observedAttributes() { 42 return ["server", "sqlite", "wasm", "dark", "vs"] 43 } 44 45 get dark() { 46 return this.hasAttribute('dark'); 47 } 48 49 set dark(value) { 50 if (value) { 51 this.rootEL!.classList.add('dark'); 52 this.setAttribute('dark', ''); 53 } else { 54 this.rootEL!.classList.remove('dark'); 55 this.removeAttribute('dark'); 56 } 57 if (this.skinChangeArray.length > 0) { 58 this.skinChangeArray.forEach((item) => item(value)); 59 } 60 if (SpApplication.skinChange) { 61 SpApplication.skinChange(value); 62 } 63 if (SpApplication.skinChange2) { 64 SpApplication.skinChange2(value); 65 } 66 } 67 68 initHtml(): string { 69 return ` 70<style> 71:host{ 72 73} 74.dark{ 75--dark-background: #272C34; 76--dark-background1: #424851; 77--dark-background2: #262f3c; 78--dark-background3: #292D33; 79--dark-background4: #323841; 80--dark-background5: #333840; 81--dark-background6: rgba(82,145,255,0.2); 82--dark-background7: #494d52; 83--dark-background8: #5291FF; 84--dark-color: rgba(255,255,255,0.6); 85--dark-color1: rgba(255,255,255,0.86); 86--dark-color2: rgba(255,255,255,0.9); 87--dark-border: #474F59; 88--dark-color3:#4694C2; 89--dark-color4:#5AADA0; 90--dark-border1: #454E5A; 91--bark-expansion:#0076FF; 92--bark-prompt:#9e9e9e; 93--dark-icon:#adafb3; 94--dark-img: url('img/dark_pic.png'); 95 background: #272C34; 96 color: #FFFFFF; 97} 98.root{ 99 display: grid; 100 grid-template-rows: min-content 1fr; 101 grid-template-columns: min-content 1fr; 102 grid-template-areas: 'm s' 103 'm b'; 104 height: 100vh; 105 width: 100vw; 106} 107.filedrag::after { 108 content: 'Drop the trace file to open it'; 109 position: fixed; 110 z-index: 101; 111 top: 0; 112 left: 0; 113 right: 0; 114 bottom: 0; 115 border: 5px dashed var(--dark-color1,#404854); 116 text-align: center; 117 font-size: 3rem; 118 line-height: 100vh; 119 background: rgba(255, 255, 255, 0.5); 120 } 121.menu{ 122 grid-area: m; 123 box-shadow: 4px 0px 20px rgba(0,0,0,0.05); 124 z-index: 101; 125} 126.search-container{ 127 z-index: 10; 128 position: relative; 129} 130.progress{ 131 bottom: 0; 132 position: absolute; 133 height: 1px; 134 left: 0; 135 right: 0; 136} 137:host(:not([search])) #sp-search { 138 display: none; 139} 140 141:host(:not([search])) .search-container .search { 142 background-color: var(--dark-background5,#F6F6F6); 143} 144.search{ 145 grid-area: s; 146 background-color: var(--dark-background,#FFFFFF); 147 height: 48px; 148 display: flex; 149 justify-content: center; 150 align-items: center; 151 152} 153.search .search-bg{ 154 background-color: var(--dark-background5,#fff); 155 border-radius: 40px; 156 padding: 3px 20px; 157 display: flex; 158 justify-content: center; 159 align-items: center; 160 border: 1px solid var(--dark-border,#c5c5c5); 161} 162.search input{ 163 outline: none; 164 border: 0px; 165 background-color: transparent; 166 font-size: inherit; 167 color: var(--dark-color,#666666); 168 width: 30vw; 169 height: auto; 170 vertical-align:middle; 171 line-height:inherit; 172 height:inherit; 173 padding: 6px 6px 6px 6px}; 174 max-height: inherit; 175 box-sizing: border-box; 176 177} 178::placeholder { 179 color: #b5b7ba; 180 font-size: 1em; 181} 182.search input::placeholder { 183 color: #b5b7ba; 184 font-size: 1em; 185} 186.content{ 187 grid-area: b; 188 background-color: #ffffff; 189 height: 100%; 190 overflow: auto; 191 position:relative; 192} 193.sheet{ 194 195} 196.sidebar-button{ 197 position: absolute; 198 top: 0; 199 left: 0; 200 background-color: var(--dark-background1,#FFFFFF); 201 height: 100%; 202 border-radius: 0 5px 5px 0; 203 width: 48px; 204 display: flex; 205 align-content: center; 206 justify-content: center; 207 cursor: pointer; 208} 209:host{ 210 font-size: inherit; 211 display: inline-block; 212 transition: .3s; 213 } 214 :host([spin]){ 215 animation: rotate 1.75s linear infinite; 216 } 217 @keyframes rotate { 218 to{ 219 transform: rotate(360deg); 220 } 221 } 222 .icon{ 223 display: block; 224 width: 1em; 225 height: 1em; 226 margin: auto; 227 fill: currentColor; 228 overflow: hidden; 229 font-size: 20px; 230 color: var(--dark-color1,#4D4D4D); 231 } 232</style> 233<div class="root"> 234 <lit-main-menu id="main-menu" class="menu" data=''> 235 </lit-main-menu> 236 <div class="search-container"> 237 <div class="search" style="position: relative;"> 238 <div class="sidebar-button" style="width: 0"> 239 <svg class="icon" id="icon" aria-hidden="true" viewBox="0 0 1024 1024"> 240 <use id="use" xlink:href="./base-ui/icon.svg#icon-menu"></use> 241 </svg> 242 </div> 243 <sp-search id="sp-search"></sp-search> 244 </div> 245 <lit-progress-bar class="progress"></lit-progress-bar> 246 </div> 247 248 <div id="app-content" class="content"> 249 <sp-welcome style="visibility:visible;top:0px;left:0px;position:absolute;z-index: 100" id="sp-welcome"></sp-welcome> 250 <sp-system-trace style="visibility:visible;" id="sp-system-trace"></sp-system-trace> 251 <sp-record-trace style="width:100%;height:100%;overflow:auto;visibility:hidden;top:0px;left:0px;right:0;bottom:0px;position:absolute;z-index: 102" id="sp-record-trace"></sp-record-trace> 252 </div> 253</div>`; 254 } 255 256 set vs(isVs: boolean) { 257 if (isVs) { 258 this.setAttribute("vs", "") 259 } 260 } 261 262 get vs(): boolean { 263 return this.hasAttribute("vs") 264 } 265 266 get sqlite(): boolean { 267 return this.hasAttribute("sqlite") 268 } 269 270 get wasm(): boolean { 271 return this.hasAttribute("wasm") 272 } 273 274 get server(): boolean { 275 return this.hasAttribute("server") 276 } 277 278 set server(s: boolean) { 279 if (s) { 280 this.setAttribute('server', '') 281 } else { 282 this.removeAttribute('server') 283 } 284 } 285 286 set search(search: boolean) { 287 if (search) { 288 this.setAttribute('search', '') 289 } else { 290 this.removeAttribute('search') 291 } 292 } 293 294 initElements() { 295 let that = this; 296 this.rootEL = this.shadowRoot!.querySelector<HTMLDivElement>(".root") 297 let spWelcomePage = this.shadowRoot!.querySelector("#sp-welcome") as SpWelcomePage 298 let spSystemTrace = this.shadowRoot!.querySelector<SpSystemTrace>("#sp-system-trace") 299 let spRecordTrace = this.shadowRoot!.querySelector<SpRecordTrace>("#sp-record-trace") 300 let mainMenu = this.shadowRoot?.querySelector('#main-menu') as LitMainMenu 301 let progressEL = this.shadowRoot?.querySelector('.progress') as LitProgressBar 302 let spSearch = this.shadowRoot?.querySelector('#sp-search') as SpSearch 303 let sidebarButton: HTMLDivElement | undefined | null = this.shadowRoot?.querySelector('.sidebar-button') 304 let childNodes = [spSystemTrace, spRecordTrace, spWelcomePage] 305 spSearch.addEventListener("focus", () => { 306 spSystemTrace!.keyboardEnable = false 307 }) 308 spSearch.addEventListener('blur', () => { 309 spSystemTrace!.keyboardEnable = true 310 }) 311 spSearch.addEventListener("enter", (e: any) => { 312 spSystemTrace!.shadowRoot?.querySelectorAll<TraceRow<any>>('trace-row').forEach(item => { 313 if (e.detail.value == null || e.detail.value == undefined || e.detail.value == '') { 314 if (item.rowType == TraceRow.ROW_TYPE_CPU || 315 item.rowType == TraceRow.ROW_TYPE_CPU_FREQ || 316 item.rowType == TraceRow.ROW_TYPE_FPS || 317 item.rowType == TraceRow.ROW_TYPE_PROCESS) { 318 item.rowHidden = false; 319 } else { 320 item.rowHidden = true; 321 } 322 } else { 323 if (item.name.toLowerCase().indexOf(e.detail.value.toLowerCase()) >= 0) { 324 item.rowHidden = false; 325 } else { 326 item.rowHidden = true; 327 } 328 } 329 }) 330 }) 331 sidebarButton!.onclick = (event) => { 332 let menu: HTMLDivElement | undefined | null = this.shadowRoot?.querySelector('#main-menu') 333 let menuButton: HTMLElement | undefined | null = this.shadowRoot?.querySelector('.sidebar-button') 334 if (menu) { 335 menu.style.width = `248px` 336 // @ts-ignore 337 menu.style.zIndex = 200; 338 menu.style.display = `flex` 339 } 340 if (menuButton) { 341 menuButton.style.width = `0px` 342 } 343 } 344 let icon: HTMLDivElement | undefined | null = this.shadowRoot?.querySelector("#main-menu")?.shadowRoot?.querySelector("div.header > div") 345 icon!.onclick = (mouseEvent) => { 346 let menu: HTMLElement | undefined | null = this.shadowRoot?.querySelector("#main-menu") 347 let menuButton: HTMLElement | undefined | null = this.shadowRoot?.querySelector('.sidebar-button') 348 if (menu) { 349 menu.style.width = `0px` 350 menu.style.display = `none` 351 // @ts-ignore 352 menu.style.zIndex = 0 353 } 354 if (menuButton) { 355 menuButton.style.width = `48px` 356 } 357 } 358 359 function showContent(showNode: HTMLElement) { 360 childNodes.forEach((node) => { 361 if (node === showNode) { 362 showNode.style.visibility = 'visible' 363 } else { 364 node!.style.visibility = 'hidden' 365 } 366 }) 367 } 368 369 function openTraceFile(ev: any) { 370 showContent(spSystemTrace!) 371 that.search = true 372 progressEL.loading = true 373 let fileName = (ev as any).name 374 let fileSize = ((ev as any).size / 1000000).toFixed(1) 375 document.title = `${fileName.substring(0, fileName.lastIndexOf('.'))} (${fileSize}M)` 376 377 if (that.server) { 378 threadPool.init("server").then(() => { 379 spSearch.setPercent("parse trace", 1); 380 const fd = new FormData() 381 that.freshMenuDisable(true) 382 fd.append('file', ev as any) 383 let uploadPath = `https://${window.location.host.split(':')[0]}:9001/upload` 384 if (that.vs) { 385 uploadPath = `http://${window.location.host.split(':')[0]}:${window.location.port}/upload` 386 } 387 fetch(uploadPath, { 388 method: 'POST', 389 body: fd, 390 }).then(res => { 391 spSearch.setPercent("load database", 5); 392 if (res.ok) { 393 mainMenu.menus!.splice(1, 1, { 394 collapsed: false, 395 title: "Current Trace", 396 describe: "Actions on the current trace", 397 children: [ 398 { 399 title: `${fileName.substring(0, fileName.lastIndexOf('.'))} (${fileSize}M)`, 400 icon: "file-fill", 401 clickHandler: function () { 402 that.search = true 403 showContent(spSystemTrace!) 404 } 405 } 406 ] 407 }) 408 that.freshMenuDisable(true) 409 return res.text(); 410 } else { 411 if (res.status == 404) { 412 spSearch.setPercent("This File is not supported!", -1) 413 progressEL.loading = false; 414 that.freshMenuDisable(false) 415 return Promise.reject(); 416 } 417 } 418 }).then(res => { 419 if (res != undefined) { 420 let loadPath = `https://${window.location.host.split(':')[0]}:9001` 421 if (that.vs) { 422 loadPath = `http://${window.location.host.split(':')[0]}:${window.location.port}` 423 } 424 spSystemTrace!.loadDatabaseUrl(loadPath + res, (command: string, percent: number) => { 425 spSearch.setPercent(command + ' ', percent); 426 }, (res) => { 427 spSearch.setPercent("", 101); 428 progressEL.loading = false; 429 that.freshMenuDisable(false) 430 }) 431 } else { 432 spSearch.setPercent("", 101) 433 progressEL.loading = false; 434 that.freshMenuDisable(false) 435 } 436 437 }) 438 }) 439 return; 440 } 441 if (that.sqlite) { 442 spSearch.setPercent("", 0); 443 threadPool.init("sqlite").then(res => { 444 let reader = new FileReader(); 445 reader.readAsArrayBuffer(ev as any) 446 reader.onloadend = function (event) { 447 spSystemTrace!.loadDatabaseArrayBuffer(this.result as ArrayBuffer, (command: string, percent: number) => { 448 spSearch.setPercent(command + ' ', percent); 449 }, () => { 450 spSearch.setPercent("", 101); 451 progressEL.loading = false; 452 that.freshMenuDisable(false) 453 }) 454 } 455 }) 456 return; 457 } 458 if (that.wasm) { 459 spSearch.setPercent("", 1); 460 threadPool.init("wasm").then(res => { 461 let reader = new FileReader(); 462 reader.readAsArrayBuffer(ev as any) 463 reader.onloadend = function (event) { 464 spSearch.setPercent("ArrayBuffer loaded ", 2); 465 that.freshMenuDisable(true) 466 mainMenu.menus!.splice(1, 1, { 467 collapsed: false, 468 title: "Current Trace", 469 describe: "Actions on the current trace", 470 children: [ 471 { 472 title: `${fileName.substring(0, fileName.lastIndexOf('.'))} (${fileSize}M)`, 473 icon: "file-fill", 474 clickHandler: function () { 475 that.search = true 476 showContent(spSystemTrace!) 477 } 478 } 479 ] 480 }) 481 spSystemTrace!.loadDatabaseArrayBuffer(this.result as ArrayBuffer, (command: string, percent: number) => { 482 spSearch.setPercent(command + ' ', percent); 483 }, (res) => { 484 if (res.status) { 485 spSearch.setPercent("", 101); 486 progressEL.loading = false; 487 that.freshMenuDisable(false) 488 } else { 489 spSearch.setPercent("", 101); 490 progressEL.loading = false; 491 that.freshMenuDisable(false) 492 alert(res.msg) 493 } 494 }) 495 } 496 }) 497 return; 498 } 499 } 500 501 mainMenu.menus = [ 502 { 503 collapsed: false, 504 title: 'Navigation', 505 describe: 'Open or record a new trace', 506 children: [ 507 { 508 title: "Open trace file", 509 icon: "folder", 510 fileChoose: true, 511 fileHandler: function (ev: InputEvent) { 512 openTraceFile(ev.detail as any); 513 } 514 }, 515 { 516 title: "Record new trace", icon: "copyhovered", clickHandler: function (item: MenuItem) { 517 that.search = false 518 showContent(spRecordTrace!) 519 } 520 } 521 ] 522 } 523 ] 524 } 525 526 freshMenuDisable(disable: boolean) { 527 let mainMenu = this.shadowRoot?.querySelector('#main-menu') as LitMainMenu 528 // @ts-ignore 529 mainMenu.menus[0].children[0].disabled = disable 530 mainMenu.menus = mainMenu.menus; 531 } 532} 533