1/* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17import {ChangeDetectorRef, Component, Inject} from '@angular/core'; 18import {FunctionUtils} from 'common/function_utils'; 19import { 20 Message, 21 MessageBugReport, 22 MessagePing, 23 MessageTimestamp, 24 MessageType, 25} from 'cross_tool/messages'; 26 27@Component({ 28 selector: 'app-root', 29 template: ` 30 <span class="app-title">Remote Tool Mock (simulates cross-tool protocol)</span> 31 32 <hr /> 33 <p>Open Winscope tab</p> 34 <input 35 class="button-open-winscope" 36 type="button" 37 value="Open" 38 (click)="onButtonOpenWinscopeClick()" /> 39 40 <hr /> 41 <p>Send bugreport</p> 42 <input 43 class="button-upload-bugreport" 44 type="file" 45 value="" 46 (change)="onUploadBugreport($event)" /> 47 48 <hr /> 49 <p>Send timestamp [ns]</p> 50 <input class="input-timestamp" type="number" id="name" name="name" /> 51 <input 52 class="button-send-timestamp" 53 type="button" 54 value="Send" 55 (click)="onButtonSendTimestampClick()" /> 56 57 <hr /> 58 <p>Received timestamp:</p> 59 <p class="paragraph-received-timestamp"></p> 60 `, 61}) 62export class AppComponent { 63 static readonly TARGET = 'http://localhost:8080'; 64 static readonly TIMESTAMP_IN_BUGREPORT_MESSAGE = 1670509911000000000n; 65 66 private winscope: Window | null = null; 67 private isWinscopeUp = false; 68 private onMessagePongReceived = FunctionUtils.DO_NOTHING; 69 70 constructor(@Inject(ChangeDetectorRef) private changeDetectorRef: ChangeDetectorRef) { 71 window.addEventListener('message', (event) => { 72 this.onMessageReceived(event); 73 }); 74 } 75 76 async onButtonOpenWinscopeClick() { 77 this.openWinscope(); 78 await this.waitWinscopeUp(); 79 } 80 81 async onUploadBugreport(event: Event) { 82 const [file, buffer] = await this.readInputFile(event); 83 this.sendBugreport(file, buffer); 84 } 85 86 onButtonSendTimestampClick() { 87 const inputTimestampElement = document.querySelector('.input-timestamp')! as HTMLInputElement; 88 this.sendTimestamp(BigInt(inputTimestampElement.value)); 89 } 90 91 private openWinscope() { 92 this.printStatus('OPENING WINSCOPE'); 93 94 this.winscope = window.open(AppComponent.TARGET); 95 if (!this.winscope) { 96 throw new Error('Failed to open winscope'); 97 } 98 99 this.printStatus('OPENED WINSCOPE'); 100 } 101 102 private async waitWinscopeUp() { 103 this.printStatus('WAITING WINSCOPE UP'); 104 105 const promise = new Promise<void>((resolve) => { 106 this.onMessagePongReceived = () => { 107 this.isWinscopeUp = true; 108 resolve(); 109 }; 110 }); 111 112 setTimeout(async () => { 113 while (!this.isWinscopeUp) { 114 this.winscope!.postMessage(new MessagePing(), AppComponent.TARGET); 115 await this.sleep(10); 116 } 117 }, 0); 118 119 await promise; 120 121 this.printStatus('DONE WAITING (WINSCOPE IS UP)'); 122 } 123 124 private sendBugreport(file: File, buffer: ArrayBuffer) { 125 this.printStatus('SENDING BUGREPORT'); 126 127 this.winscope!.postMessage( 128 new MessageBugReport(file, AppComponent.TIMESTAMP_IN_BUGREPORT_MESSAGE), 129 AppComponent.TARGET 130 ); 131 132 this.printStatus('SENT BUGREPORT'); 133 } 134 135 private sendTimestamp(value: bigint) { 136 this.printStatus('SENDING TIMESTAMP'); 137 138 this.winscope!.postMessage(new MessageTimestamp(value), AppComponent.TARGET); 139 140 this.printStatus('SENT TIMESTAMP'); 141 } 142 143 private onMessageReceived(event: MessageEvent) { 144 const message = event.data as Message; 145 if (!message.type) { 146 console.log('Cross-tool protocol received unrecognized message:', message); 147 return; 148 } 149 150 switch (message.type) { 151 case MessageType.PING: 152 console.log('Cross-tool protocol received unexpected ping message:', message); 153 break; 154 case MessageType.PONG: 155 this.onMessagePongReceived(); 156 break; 157 case MessageType.BUGREPORT: 158 console.log('Cross-tool protocol received unexpected bugreport message:', message); 159 break; 160 case MessageType.TIMESTAMP: 161 console.log('Cross-tool protocol received timestamp message:', message); 162 this.onMessageTimestampReceived(message as MessageTimestamp); 163 break; 164 case MessageType.FILES: 165 console.log('Cross-tool protocol received unexpected files message:', message); 166 break; 167 default: 168 console.log('Cross-tool protocol received unrecognized message:', message); 169 break; 170 } 171 } 172 173 private onMessageTimestampReceived(message: MessageTimestamp) { 174 const paragraph = document.querySelector( 175 '.paragraph-received-timestamp' 176 ) as HTMLParagraphElement; 177 paragraph.textContent = message.timestampNs.toString(); 178 this.changeDetectorRef.detectChanges(); 179 } 180 181 private printStatus(status: string) { 182 console.log('STATUS: ' + status); 183 } 184 185 private sleep(ms: number): Promise<void> { 186 return new Promise((resolve) => setTimeout(resolve, ms)); 187 } 188 189 private async readInputFile(event: Event): Promise<[File, ArrayBuffer]> { 190 const files: FileList | null = (event?.target as HTMLInputElement)?.files; 191 192 if (!files || !files[0]) { 193 throw new Error('Failed to read input files'); 194 } 195 196 return [files[0], await files[0].arrayBuffer()]; 197 } 198} 199