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 { SpFlagHtml } from './SpFlag.html'; 18 19@element('sp-flags') 20export class SpFlags extends BaseElement { 21 private bodyEl: HTMLElement | undefined | null; 22 23 initElements(): void { 24 let parentElement = this.parentNode as HTMLElement; 25 parentElement.style.overflow = 'hidden'; 26 this.bodyEl = this.shadowRoot?.querySelector('.body'); 27 this.initConfigList(); 28 } 29 30 initHtml(): string { 31 return SpFlagHtml; 32 } 33 34 private createConfigDiv(): HTMLDivElement { 35 let configDiv = document.createElement('div'); 36 configDiv.className = 'flag-widget'; 37 return configDiv; 38 } 39 40 private createCustomDiv(config: FlagConfigItem, configDiv: HTMLDivElement): void { 41 let configHadDiv = document.createElement('div'); 42 configHadDiv.className = 'flag-head-div'; 43 let titleLabel = document.createElement('label'); 44 titleLabel.textContent = config.title; 45 titleLabel.className = 'flag-title-label'; 46 let configSelect = document.createElement('select'); 47 configSelect.className = 'flag-select'; 48 configSelect.setAttribute('title', config.title); 49 config.switchOptions.forEach((optionItem) => { 50 let configOption = document.createElement('option'); 51 configOption.value = optionItem.option; 52 configOption.textContent = optionItem.option; 53 if (optionItem.selected) { 54 configOption.selected = true; 55 } 56 configSelect.appendChild(configOption); 57 }); 58 configSelect.addEventListener('change', () => { 59 let title = configSelect.getAttribute('title'); 60 FlagsConfig.updateFlagsConfig(title!, configSelect.selectedOptions[0].value); 61 if (title === 'VSync' && configSelect.selectedOptions[0].value === 'Enabled') { 62 let vsyncSelect = this.shadowRoot?.querySelector('#vsyncSelect'); 63 vsyncSelect?.removeAttribute('disabled'); 64 } 65 if (title === 'VSync' && configSelect.selectedOptions[0].value === 'Disabled') { 66 let vsyncSelect = this.shadowRoot?.querySelector('#vsyncSelect'); 67 vsyncSelect?.childNodes.forEach((child: ChildNode) => { 68 let selectEl = child as HTMLOptionElement; 69 if (child.textContent === 'VsyncGenerator') { 70 selectEl.selected = true; 71 FlagsConfig.updateFlagsConfig('vsyncValue', selectEl.value); 72 } else { 73 selectEl.selected = false; 74 } 75 }); 76 77 vsyncSelect?.setAttribute('disabled', 'disabled'); 78 } 79 }); 80 let description = document.createElement('div'); 81 description.className = 'flag-des-div'; 82 description.textContent = config.describeContent; 83 configHadDiv.appendChild(titleLabel); 84 configHadDiv.appendChild(configSelect); 85 configDiv.appendChild(configHadDiv); 86 configDiv.appendChild(description); 87 } 88 89 private initConfigList(): void { 90 let allConfig = FlagsConfig.getAllFlagConfig(); 91 allConfig.forEach((config) => { 92 let configDiv = this.createConfigDiv(); 93 this.createCustomDiv(config, configDiv); 94 if (config.title === 'AnimationAnalysis') { 95 let configFooterDiv = document.createElement('div'); 96 configFooterDiv.className = 'config_footer'; 97 let deviceWidthLabelEl = document.createElement('label'); 98 deviceWidthLabelEl.className = 'device_label'; 99 deviceWidthLabelEl.textContent = 'PhysicalWidth :'; 100 let deviceWidthEl = document.createElement('input'); 101 deviceWidthEl.value = <string>config.addInfo!.physicalWidth; 102 deviceWidthEl.addEventListener('keyup', () => { 103 deviceWidthEl.value = deviceWidthEl.value.replace(/\D/g, ''); 104 }); 105 deviceWidthEl.addEventListener('blur', () => { 106 if (deviceWidthEl.value !== '') { 107 FlagsConfig.updateFlagsConfig('physicalWidth', Number(deviceWidthEl.value)); 108 } 109 }); 110 deviceWidthEl.className = 'device_input'; 111 let deviceHeightLabelEl = document.createElement('label'); 112 deviceHeightLabelEl.textContent = 'PhysicalHeight :'; 113 deviceHeightLabelEl.className = 'device_label'; 114 let deviceHeightEl = document.createElement('input'); 115 deviceHeightEl.className = 'device_input'; 116 deviceHeightEl.value = <string>config.addInfo!.physicalHeight; 117 deviceHeightEl.addEventListener('keyup', () => { 118 deviceHeightEl.value = deviceHeightEl.value.replace(/\D/g, ''); 119 }); 120 deviceHeightEl.addEventListener('blur', () => { 121 if (deviceWidthEl.value !== '') { 122 FlagsConfig.updateFlagsConfig('physicalHeight', Number(deviceHeightEl.value)); 123 } 124 }); 125 configFooterDiv.appendChild(deviceWidthLabelEl); 126 configFooterDiv.appendChild(deviceWidthEl); 127 configFooterDiv.appendChild(deviceHeightLabelEl); 128 configFooterDiv.appendChild(deviceHeightEl); 129 configDiv.appendChild(configFooterDiv); 130 } 131 132 if (config.title === 'VSync') { 133 let configFooterDiv = this.createVsyncOption(); 134 configDiv.appendChild(configFooterDiv); 135 } 136 137 this.bodyEl!.appendChild(configDiv); 138 }); 139 } 140 141 private createVsyncOption(): HTMLDivElement { 142 let configFooterDiv = document.createElement('div'); 143 configFooterDiv.className = 'config_footer'; 144 let vsyncLableEl = document.createElement('lable'); 145 vsyncLableEl.className = 'vsync_lable'; 146 let vsyncTypeEl = document.createElement('select'); 147 vsyncTypeEl.setAttribute('id', 'vsyncSelect'); 148 vsyncTypeEl.className = 'flag-select'; 149 let vsyncGenOption = document.createElement('option'); // VsyncGeneratior = H:VsyncGenerator 150 vsyncGenOption.value = 'H:VsyncGenerator'; 151 vsyncGenOption.textContent = 'VsyncGenerator'; 152 vsyncGenOption.selected = true; 153 vsyncTypeEl.appendChild(vsyncGenOption); 154 155 let vsyncRsOption = document.createElement('option'); // Vsync-rs = H:rs_SendVsync 156 vsyncRsOption.value = 'H:rs_SendVsync'; 157 vsyncRsOption.textContent = 'Vsync-rs'; 158 vsyncTypeEl.appendChild(vsyncRsOption); 159 160 let vsyncAppOption = document.createElement('option'); // Vsync-app = H:app_SendVsync 161 vsyncAppOption.value = 'H:app_SendVsync'; 162 vsyncAppOption.textContent = 'Vsync-app'; 163 vsyncTypeEl.appendChild(vsyncAppOption); 164 165 FlagsConfig.updateFlagsConfig('vsyncValue', vsyncGenOption.value); 166 vsyncTypeEl.addEventListener('change', function () { 167 let selectValue = this.selectedOptions[0].value; 168 console.log(this); 169 console.log(this.selectedOptions[0]); 170 console.log(this.selectedOptions[0].value); 171 FlagsConfig.updateFlagsConfig('vsyncValue', selectValue); 172 }); 173 174 let flagsItem = window.localStorage.getItem(FlagsConfig.FLAGS_CONFIG_KEY); 175 let flagsItemJson = JSON.parse(flagsItem!); 176 let vsync = flagsItemJson.VSync; 177 if (vsync === 'Enabled') { 178 vsyncTypeEl.removeAttribute('disabled'); 179 } else { 180 vsyncTypeEl.setAttribute('disabled', 'disabled'); 181 FlagsConfig.updateFlagsConfig('vsyncValue', vsyncGenOption.value); 182 } 183 configFooterDiv.appendChild(vsyncLableEl); 184 configFooterDiv.appendChild(vsyncTypeEl); 185 return configFooterDiv; 186 } 187} 188 189export type Params = { 190 [key: string]: unknown; 191}; 192 193export class FlagsConfig { 194 static FLAGS_CONFIG_KEY = 'FlagsConfig'; 195 static DEFAULT_CONFIG: Array<FlagConfigItem> = [ 196 { 197 title: 'TaskPool', 198 switchOptions: [{ option: 'Enabled' }, { option: 'Disabled', selected: true }], 199 describeContent: 'Analyze TaskPool templates', 200 }, 201 { 202 title: 'AnimationAnalysis', 203 switchOptions: [{ option: 'Enabled' }, { option: 'Disabled', selected: true }], 204 describeContent: 'Analyze Animation effect templates', 205 addInfo: { physicalWidth: 0, physicalHeight: 0 }, 206 }, 207 { 208 title: 'AppStartup', 209 switchOptions: [{ option: 'Enabled' }, { option: 'Disabled', selected: true }], 210 describeContent: 'App Startup templates', 211 }, 212 { 213 title: 'SchedulingAnalysis', 214 switchOptions: [{ option: 'Enabled' }, { option: 'Disabled', selected: true }], 215 describeContent: 'Scheduling analysis templates', 216 }, 217 { 218 title: 'BinderRunnable', 219 switchOptions: [{ option: 'Enabled' }, { option: 'Disabled', selected: true }], 220 describeContent: 'support Cpu State Binder-Runnable', 221 }, 222 { 223 title: 'FfrtConvert', 224 switchOptions: [{ option: 'Enabled' }, { option: 'Disabled', selected: true }], 225 describeContent: 'Ffrt Convert templates', 226 }, 227 { 228 title: 'Bpftrace', 229 switchOptions: [{ option: 'Enabled' }, { option: 'Disabled', selected: true }], 230 describeContent: '', 231 }, 232 { 233 title: 'HMKernel', 234 switchOptions: [{ option: 'Enabled' }, { option: 'Disabled', selected: true }], 235 describeContent: '', 236 }, 237 { 238 title: 'VSync', 239 switchOptions: [{ option: 'Enabled' }, { option: 'Disabled', selected: true }], 240 describeContent: 'VSync Signal drawing', 241 }, 242 { 243 title: 'LTPO', 244 switchOptions: [{ option: 'Enabled' }, { option: 'Disabled', selected: true }], 245 describeContent: 'Lost Frame and HitchTime templates', 246 }, 247 ]; 248 249 static getAllFlagConfig(): Array<FlagConfigItem> { 250 let flagsConfigStr = window.localStorage.getItem(FlagsConfig.FLAGS_CONFIG_KEY); 251 if (flagsConfigStr === null) { 252 let flagConfigObj: Params = {}; 253 FlagsConfig.DEFAULT_CONFIG.forEach((config) => { 254 let selectedOption = config.switchOptions.filter((option) => { 255 return option.selected; 256 }); 257 let value = config.switchOptions[0].option; 258 if (selectedOption[0] !== undefined) { 259 value = selectedOption[0].option; 260 } 261 flagConfigObj[config.title] = value; 262 if (config.addInfo) { 263 for (const [key, value] of Object.entries(config.addInfo)) { 264 flagConfigObj[key] = value; 265 } 266 } 267 }); 268 window.localStorage.setItem(FlagsConfig.FLAGS_CONFIG_KEY, JSON.stringify(flagConfigObj)); 269 return FlagsConfig.DEFAULT_CONFIG; 270 } else { 271 let flagsConfig = JSON.parse(flagsConfigStr); 272 FlagsConfig.DEFAULT_CONFIG.forEach((config) => { 273 let cfg = flagsConfig[config.title]; 274 if (cfg) { 275 config.switchOptions.forEach((option) => { 276 if (option.option === cfg) { 277 option.selected = true; 278 } else { 279 option.selected = false; 280 } 281 }); 282 } 283 if (config.addInfo) { 284 for (const [key, value] of Object.entries(config.addInfo)) { 285 let cfg = flagsConfig[key]; 286 if (cfg) { 287 config.addInfo[key] = cfg; 288 } 289 } 290 } 291 }); 292 } 293 return FlagsConfig.DEFAULT_CONFIG; 294 } 295 296 static getSpTraceStreamParseConfig(): string { 297 let parseConfig = {}; 298 FlagsConfig.getAllFlagConfig().forEach((configItem) => { 299 let selectedOption = configItem.switchOptions.filter((option) => { 300 return option.selected; 301 }); 302 // @ts-ignore 303 parseConfig[configItem.title] = selectedOption[0].option === 'Enabled' ? 1 : 0; 304 }); 305 return JSON.stringify({ config: parseConfig }); 306 } 307 308 static getFlagsConfig(flagName: string): Params | undefined { 309 let flagConfigObj: Params = {}; 310 let configItem = FlagsConfig.getAllFlagConfig().find((config) => { 311 return config.title === flagName; 312 }); 313 if (configItem) { 314 let selectedOption = configItem.switchOptions.filter((option) => { 315 return option.selected; 316 }); 317 let value = configItem.switchOptions[0].option; 318 if (selectedOption[0] !== undefined) { 319 value = selectedOption[0].option; 320 } 321 flagConfigObj[configItem.title] = value; 322 if (configItem.addInfo) { 323 for (const [key, value] of Object.entries(configItem.addInfo)) { 324 flagConfigObj[key] = value; 325 } 326 } 327 return flagConfigObj; 328 } else { 329 return configItem; 330 } 331 } 332 333 static getFlagsConfigEnableStatus(flagName: string): boolean { 334 let config = FlagsConfig.getFlagsConfig(flagName); 335 let enable: boolean = false; 336 if (config && config[flagName]) { 337 enable = config[flagName] === 'Enabled'; 338 } 339 return enable; 340 } 341 342 static updateFlagsConfig(key: string, value: unknown): void { 343 let flagsConfigStr = window.localStorage.getItem(FlagsConfig.FLAGS_CONFIG_KEY); 344 let flagConfigObj: Params = {}; 345 if (flagsConfigStr !== null) { 346 flagConfigObj = JSON.parse(flagsConfigStr); 347 } 348 flagConfigObj[key] = value; 349 window.localStorage.setItem(FlagsConfig.FLAGS_CONFIG_KEY, JSON.stringify(flagConfigObj)); 350 } 351} 352 353export interface FlagConfigItem { 354 title: string; 355 switchOptions: OptionItem[]; 356 describeContent: string; 357 addInfo?: Params; 358} 359 360export interface OptionItem { 361 option: string; 362 selected?: boolean; 363} 364