1*90c8c64dSAndroid Build Coastguard Worker/* 2*90c8c64dSAndroid Build Coastguard Worker * Copyright (C) 2022 The Android Open Source Project 3*90c8c64dSAndroid Build Coastguard Worker * 4*90c8c64dSAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License"); 5*90c8c64dSAndroid Build Coastguard Worker * you may not use this file except in compliance with the License. 6*90c8c64dSAndroid Build Coastguard Worker * You may obtain a copy of the License at 7*90c8c64dSAndroid Build Coastguard Worker * 8*90c8c64dSAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0 9*90c8c64dSAndroid Build Coastguard Worker * 10*90c8c64dSAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software 11*90c8c64dSAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS, 12*90c8c64dSAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13*90c8c64dSAndroid Build Coastguard Worker * See the License for the specific language governing permissions and 14*90c8c64dSAndroid Build Coastguard Worker * limitations under the License. 15*90c8c64dSAndroid Build Coastguard Worker */ 16*90c8c64dSAndroid Build Coastguard Worker 17*90c8c64dSAndroid Build Coastguard Workerimport {assertDefined} from 'common/assert_utils'; 18*90c8c64dSAndroid Build Coastguard Workerimport {Store} from 'common/store'; 19*90c8c64dSAndroid Build Coastguard Workerimport {Timestamp} from 'common/time'; 20*90c8c64dSAndroid Build Coastguard Workerimport {TimeUtils} from 'common/time_utils'; 21*90c8c64dSAndroid Build Coastguard Workerimport {UserNotifier} from 'common/user_notifier'; 22*90c8c64dSAndroid Build Coastguard Workerimport {CrossToolProtocol} from 'cross_tool/cross_tool_protocol'; 23*90c8c64dSAndroid Build Coastguard Workerimport {Analytics} from 'logging/analytics'; 24*90c8c64dSAndroid Build Coastguard Workerimport {ProgressListener} from 'messaging/progress_listener'; 25*90c8c64dSAndroid Build Coastguard Workerimport {UserWarning} from 'messaging/user_warning'; 26*90c8c64dSAndroid Build Coastguard Workerimport { 27*90c8c64dSAndroid Build Coastguard Worker CannotVisualizeTraceEntry, 28*90c8c64dSAndroid Build Coastguard Worker FailedToInitializeTimelineData, 29*90c8c64dSAndroid Build Coastguard Worker IncompleteFrameMapping, 30*90c8c64dSAndroid Build Coastguard Worker NoTraceTargetsSelected, 31*90c8c64dSAndroid Build Coastguard Worker NoValidFiles, 32*90c8c64dSAndroid Build Coastguard Worker} from 'messaging/user_warnings'; 33*90c8c64dSAndroid Build Coastguard Workerimport { 34*90c8c64dSAndroid Build Coastguard Worker ActiveTraceChanged, 35*90c8c64dSAndroid Build Coastguard Worker ExpandedTimelineToggled, 36*90c8c64dSAndroid Build Coastguard Worker TraceAddRequest, 37*90c8c64dSAndroid Build Coastguard Worker TracePositionUpdate, 38*90c8c64dSAndroid Build Coastguard Worker TraceSearchCompleted, 39*90c8c64dSAndroid Build Coastguard Worker TraceSearchFailed, 40*90c8c64dSAndroid Build Coastguard Worker TraceSearchInitialized, 41*90c8c64dSAndroid Build Coastguard Worker ViewersLoaded, 42*90c8c64dSAndroid Build Coastguard Worker ViewersUnloaded, 43*90c8c64dSAndroid Build Coastguard Worker WinscopeEvent, 44*90c8c64dSAndroid Build Coastguard Worker WinscopeEventType, 45*90c8c64dSAndroid Build Coastguard Worker} from 'messaging/winscope_event'; 46*90c8c64dSAndroid Build Coastguard Workerimport {WinscopeEventEmitter} from 'messaging/winscope_event_emitter'; 47*90c8c64dSAndroid Build Coastguard Workerimport {WinscopeEventListener} from 'messaging/winscope_event_listener'; 48*90c8c64dSAndroid Build Coastguard Workerimport {TraceEntry} from 'trace/trace'; 49*90c8c64dSAndroid Build Coastguard Workerimport {TRACE_INFO} from 'trace/trace_info'; 50*90c8c64dSAndroid Build Coastguard Workerimport {TracePosition} from 'trace/trace_position'; 51*90c8c64dSAndroid Build Coastguard Workerimport {TraceType} from 'trace/trace_type'; 52*90c8c64dSAndroid Build Coastguard Workerimport {RequestedTraceTypes} from 'trace_collection/adb_files'; 53*90c8c64dSAndroid Build Coastguard Workerimport {View, Viewer, ViewType} from 'viewers/viewer'; 54*90c8c64dSAndroid Build Coastguard Workerimport {ViewerFactory} from 'viewers/viewer_factory'; 55*90c8c64dSAndroid Build Coastguard Workerimport {FilesSource} from './files_source'; 56*90c8c64dSAndroid Build Coastguard Workerimport {TimelineData} from './timeline_data'; 57*90c8c64dSAndroid Build Coastguard Workerimport {TracePipeline} from './trace_pipeline'; 58*90c8c64dSAndroid Build Coastguard Workerimport {TraceSearchInitializer} from './trace_search/trace_search_initializer'; 59*90c8c64dSAndroid Build Coastguard Worker 60*90c8c64dSAndroid Build Coastguard Workerexport class Mediator { 61*90c8c64dSAndroid Build Coastguard Worker private abtChromeExtensionProtocol: WinscopeEventEmitter & 62*90c8c64dSAndroid Build Coastguard Worker WinscopeEventListener; 63*90c8c64dSAndroid Build Coastguard Worker private crossToolProtocol: CrossToolProtocol; 64*90c8c64dSAndroid Build Coastguard Worker private uploadTracesComponent?: ProgressListener; 65*90c8c64dSAndroid Build Coastguard Worker private collectTracesComponent?: ProgressListener & 66*90c8c64dSAndroid Build Coastguard Worker WinscopeEventEmitter & 67*90c8c64dSAndroid Build Coastguard Worker WinscopeEventListener; 68*90c8c64dSAndroid Build Coastguard Worker private traceViewComponent?: WinscopeEventEmitter & WinscopeEventListener; 69*90c8c64dSAndroid Build Coastguard Worker private timelineComponent?: WinscopeEventEmitter & WinscopeEventListener; 70*90c8c64dSAndroid Build Coastguard Worker private appComponent: WinscopeEventListener; 71*90c8c64dSAndroid Build Coastguard Worker private storage: Store; 72*90c8c64dSAndroid Build Coastguard Worker 73*90c8c64dSAndroid Build Coastguard Worker private tracePipeline: TracePipeline; 74*90c8c64dSAndroid Build Coastguard Worker private timelineData: TimelineData; 75*90c8c64dSAndroid Build Coastguard Worker private viewers: Viewer[] = []; 76*90c8c64dSAndroid Build Coastguard Worker private focusedTabView: undefined | View; 77*90c8c64dSAndroid Build Coastguard Worker private areViewersLoaded = false; 78*90c8c64dSAndroid Build Coastguard Worker private lastRemoteToolDeferredTimestampReceived?: () => Timestamp | undefined; 79*90c8c64dSAndroid Build Coastguard Worker private currentProgressListener?: ProgressListener; 80*90c8c64dSAndroid Build Coastguard Worker 81*90c8c64dSAndroid Build Coastguard Worker constructor( 82*90c8c64dSAndroid Build Coastguard Worker tracePipeline: TracePipeline, 83*90c8c64dSAndroid Build Coastguard Worker timelineData: TimelineData, 84*90c8c64dSAndroid Build Coastguard Worker abtChromeExtensionProtocol: WinscopeEventEmitter & WinscopeEventListener, 85*90c8c64dSAndroid Build Coastguard Worker crossToolProtocol: CrossToolProtocol, 86*90c8c64dSAndroid Build Coastguard Worker appComponent: WinscopeEventListener, 87*90c8c64dSAndroid Build Coastguard Worker storage: Store, 88*90c8c64dSAndroid Build Coastguard Worker ) { 89*90c8c64dSAndroid Build Coastguard Worker this.tracePipeline = tracePipeline; 90*90c8c64dSAndroid Build Coastguard Worker this.timelineData = timelineData; 91*90c8c64dSAndroid Build Coastguard Worker this.abtChromeExtensionProtocol = abtChromeExtensionProtocol; 92*90c8c64dSAndroid Build Coastguard Worker this.crossToolProtocol = crossToolProtocol; 93*90c8c64dSAndroid Build Coastguard Worker this.appComponent = appComponent; 94*90c8c64dSAndroid Build Coastguard Worker this.storage = storage; 95*90c8c64dSAndroid Build Coastguard Worker 96*90c8c64dSAndroid Build Coastguard Worker this.crossToolProtocol.setEmitEvent(async (event) => { 97*90c8c64dSAndroid Build Coastguard Worker await this.onWinscopeEvent(event); 98*90c8c64dSAndroid Build Coastguard Worker }); 99*90c8c64dSAndroid Build Coastguard Worker 100*90c8c64dSAndroid Build Coastguard Worker this.abtChromeExtensionProtocol.setEmitEvent(async (event) => { 101*90c8c64dSAndroid Build Coastguard Worker await this.onWinscopeEvent(event); 102*90c8c64dSAndroid Build Coastguard Worker }); 103*90c8c64dSAndroid Build Coastguard Worker } 104*90c8c64dSAndroid Build Coastguard Worker 105*90c8c64dSAndroid Build Coastguard Worker setUploadTracesComponent(component: ProgressListener | undefined) { 106*90c8c64dSAndroid Build Coastguard Worker this.uploadTracesComponent = component; 107*90c8c64dSAndroid Build Coastguard Worker } 108*90c8c64dSAndroid Build Coastguard Worker 109*90c8c64dSAndroid Build Coastguard Worker setCollectTracesComponent( 110*90c8c64dSAndroid Build Coastguard Worker component: 111*90c8c64dSAndroid Build Coastguard Worker | (ProgressListener & WinscopeEventEmitter & WinscopeEventListener) 112*90c8c64dSAndroid Build Coastguard Worker | undefined, 113*90c8c64dSAndroid Build Coastguard Worker ) { 114*90c8c64dSAndroid Build Coastguard Worker this.collectTracesComponent = component; 115*90c8c64dSAndroid Build Coastguard Worker this.collectTracesComponent?.setEmitEvent(async (event) => { 116*90c8c64dSAndroid Build Coastguard Worker await this.onWinscopeEvent(event); 117*90c8c64dSAndroid Build Coastguard Worker }); 118*90c8c64dSAndroid Build Coastguard Worker } 119*90c8c64dSAndroid Build Coastguard Worker 120*90c8c64dSAndroid Build Coastguard Worker setTraceViewComponent( 121*90c8c64dSAndroid Build Coastguard Worker component: (WinscopeEventEmitter & WinscopeEventListener) | undefined, 122*90c8c64dSAndroid Build Coastguard Worker ) { 123*90c8c64dSAndroid Build Coastguard Worker this.traceViewComponent = component; 124*90c8c64dSAndroid Build Coastguard Worker this.traceViewComponent?.setEmitEvent(async (event) => { 125*90c8c64dSAndroid Build Coastguard Worker await this.onWinscopeEvent(event); 126*90c8c64dSAndroid Build Coastguard Worker }); 127*90c8c64dSAndroid Build Coastguard Worker } 128*90c8c64dSAndroid Build Coastguard Worker 129*90c8c64dSAndroid Build Coastguard Worker setTimelineComponent( 130*90c8c64dSAndroid Build Coastguard Worker component: (WinscopeEventEmitter & WinscopeEventListener) | undefined, 131*90c8c64dSAndroid Build Coastguard Worker ) { 132*90c8c64dSAndroid Build Coastguard Worker this.timelineComponent = component; 133*90c8c64dSAndroid Build Coastguard Worker this.timelineComponent?.setEmitEvent(async (event) => { 134*90c8c64dSAndroid Build Coastguard Worker await this.onWinscopeEvent(event); 135*90c8c64dSAndroid Build Coastguard Worker }); 136*90c8c64dSAndroid Build Coastguard Worker } 137*90c8c64dSAndroid Build Coastguard Worker 138*90c8c64dSAndroid Build Coastguard Worker async onWinscopeEvent(event: WinscopeEvent) { 139*90c8c64dSAndroid Build Coastguard Worker await event.visit(WinscopeEventType.APP_INITIALIZED, async (event) => { 140*90c8c64dSAndroid Build Coastguard Worker await this.abtChromeExtensionProtocol.onWinscopeEvent(event); 141*90c8c64dSAndroid Build Coastguard Worker }); 142*90c8c64dSAndroid Build Coastguard Worker 143*90c8c64dSAndroid Build Coastguard Worker await event.visit(WinscopeEventType.APP_FILES_UPLOADED, async (event) => { 144*90c8c64dSAndroid Build Coastguard Worker this.currentProgressListener = this.uploadTracesComponent; 145*90c8c64dSAndroid Build Coastguard Worker await this.loadFiles(event.files, FilesSource.UPLOADED); 146*90c8c64dSAndroid Build Coastguard Worker UserNotifier.notify(); 147*90c8c64dSAndroid Build Coastguard Worker }); 148*90c8c64dSAndroid Build Coastguard Worker 149*90c8c64dSAndroid Build Coastguard Worker await event.visit(WinscopeEventType.APP_FILES_COLLECTED, async (event) => { 150*90c8c64dSAndroid Build Coastguard Worker this.currentProgressListener = this.collectTracesComponent; 151*90c8c64dSAndroid Build Coastguard Worker if (event.files.collected.length > 0) { 152*90c8c64dSAndroid Build Coastguard Worker await this.loadFiles(event.files.collected, FilesSource.COLLECTED); 153*90c8c64dSAndroid Build Coastguard Worker const traces = this.tracePipeline.getTraces(); 154*90c8c64dSAndroid Build Coastguard Worker if (traces.getSize() > 0) { 155*90c8c64dSAndroid Build Coastguard Worker const failedTraces: string[] = []; 156*90c8c64dSAndroid Build Coastguard Worker event.files.requested.forEach((requested: RequestedTraceTypes) => { 157*90c8c64dSAndroid Build Coastguard Worker if ( 158*90c8c64dSAndroid Build Coastguard Worker !requested.types.some((type) => traces.getTraces(type).length > 0) 159*90c8c64dSAndroid Build Coastguard Worker ) { 160*90c8c64dSAndroid Build Coastguard Worker failedTraces.push(requested.name); 161*90c8c64dSAndroid Build Coastguard Worker } 162*90c8c64dSAndroid Build Coastguard Worker }); 163*90c8c64dSAndroid Build Coastguard Worker if (failedTraces.length > 0) { 164*90c8c64dSAndroid Build Coastguard Worker UserNotifier.add(new NoValidFiles(failedTraces)); 165*90c8c64dSAndroid Build Coastguard Worker } 166*90c8c64dSAndroid Build Coastguard Worker await this.loadViewers(); 167*90c8c64dSAndroid Build Coastguard Worker } else { 168*90c8c64dSAndroid Build Coastguard Worker this.currentProgressListener?.onOperationFinished(false); 169*90c8c64dSAndroid Build Coastguard Worker } 170*90c8c64dSAndroid Build Coastguard Worker } else { 171*90c8c64dSAndroid Build Coastguard Worker UserNotifier.add(new NoValidFiles()); 172*90c8c64dSAndroid Build Coastguard Worker } 173*90c8c64dSAndroid Build Coastguard Worker UserNotifier.notify(); 174*90c8c64dSAndroid Build Coastguard Worker }); 175*90c8c64dSAndroid Build Coastguard Worker 176*90c8c64dSAndroid Build Coastguard Worker await event.visit(WinscopeEventType.APP_RESET_REQUEST, async () => { 177*90c8c64dSAndroid Build Coastguard Worker await this.resetAppToInitialState(); 178*90c8c64dSAndroid Build Coastguard Worker }); 179*90c8c64dSAndroid Build Coastguard Worker 180*90c8c64dSAndroid Build Coastguard Worker await event.visit( 181*90c8c64dSAndroid Build Coastguard Worker WinscopeEventType.APP_REFRESH_DUMPS_REQUEST, 182*90c8c64dSAndroid Build Coastguard Worker async (event) => { 183*90c8c64dSAndroid Build Coastguard Worker await this.resetAppToInitialState(); 184*90c8c64dSAndroid Build Coastguard Worker await this.collectTracesComponent?.onWinscopeEvent(event); 185*90c8c64dSAndroid Build Coastguard Worker }, 186*90c8c64dSAndroid Build Coastguard Worker ); 187*90c8c64dSAndroid Build Coastguard Worker 188*90c8c64dSAndroid Build Coastguard Worker await event.visit(WinscopeEventType.APP_TRACE_VIEW_REQUEST, async () => { 189*90c8c64dSAndroid Build Coastguard Worker await this.loadViewers(); 190*90c8c64dSAndroid Build Coastguard Worker UserNotifier.notify(); 191*90c8c64dSAndroid Build Coastguard Worker }); 192*90c8c64dSAndroid Build Coastguard Worker 193*90c8c64dSAndroid Build Coastguard Worker await event.visit( 194*90c8c64dSAndroid Build Coastguard Worker WinscopeEventType.REMOTE_TOOL_DOWNLOAD_START, 195*90c8c64dSAndroid Build Coastguard Worker async () => { 196*90c8c64dSAndroid Build Coastguard Worker Analytics.Tracing.logOpenFromABT(); 197*90c8c64dSAndroid Build Coastguard Worker await this.resetAppToInitialState(); 198*90c8c64dSAndroid Build Coastguard Worker this.currentProgressListener = this.uploadTracesComponent; 199*90c8c64dSAndroid Build Coastguard Worker this.currentProgressListener?.onProgressUpdate( 200*90c8c64dSAndroid Build Coastguard Worker 'Downloading files...', 201*90c8c64dSAndroid Build Coastguard Worker undefined, 202*90c8c64dSAndroid Build Coastguard Worker ); 203*90c8c64dSAndroid Build Coastguard Worker }, 204*90c8c64dSAndroid Build Coastguard Worker ); 205*90c8c64dSAndroid Build Coastguard Worker 206*90c8c64dSAndroid Build Coastguard Worker await event.visit( 207*90c8c64dSAndroid Build Coastguard Worker WinscopeEventType.REMOTE_TOOL_FILES_RECEIVED, 208*90c8c64dSAndroid Build Coastguard Worker async (event) => { 209*90c8c64dSAndroid Build Coastguard Worker await this.processRemoteFilesReceived( 210*90c8c64dSAndroid Build Coastguard Worker event.files, 211*90c8c64dSAndroid Build Coastguard Worker FilesSource.REMOTE_TOOL, 212*90c8c64dSAndroid Build Coastguard Worker ); 213*90c8c64dSAndroid Build Coastguard Worker if (event.deferredTimestamp) { 214*90c8c64dSAndroid Build Coastguard Worker await this.processRemoteToolDeferredTimestampReceived( 215*90c8c64dSAndroid Build Coastguard Worker event.deferredTimestamp, 216*90c8c64dSAndroid Build Coastguard Worker ); 217*90c8c64dSAndroid Build Coastguard Worker } 218*90c8c64dSAndroid Build Coastguard Worker }, 219*90c8c64dSAndroid Build Coastguard Worker ); 220*90c8c64dSAndroid Build Coastguard Worker 221*90c8c64dSAndroid Build Coastguard Worker await event.visit( 222*90c8c64dSAndroid Build Coastguard Worker WinscopeEventType.REMOTE_TOOL_TIMESTAMP_RECEIVED, 223*90c8c64dSAndroid Build Coastguard Worker async (event) => { 224*90c8c64dSAndroid Build Coastguard Worker await this.processRemoteToolDeferredTimestampReceived( 225*90c8c64dSAndroid Build Coastguard Worker event.deferredTimestamp, 226*90c8c64dSAndroid Build Coastguard Worker ); 227*90c8c64dSAndroid Build Coastguard Worker }, 228*90c8c64dSAndroid Build Coastguard Worker ); 229*90c8c64dSAndroid Build Coastguard Worker 230*90c8c64dSAndroid Build Coastguard Worker await event.visit( 231*90c8c64dSAndroid Build Coastguard Worker WinscopeEventType.TABBED_VIEW_SWITCH_REQUEST, 232*90c8c64dSAndroid Build Coastguard Worker async (event) => { 233*90c8c64dSAndroid Build Coastguard Worker await this.traceViewComponent?.onWinscopeEvent(event); 234*90c8c64dSAndroid Build Coastguard Worker }, 235*90c8c64dSAndroid Build Coastguard Worker ); 236*90c8c64dSAndroid Build Coastguard Worker 237*90c8c64dSAndroid Build Coastguard Worker await event.visit(WinscopeEventType.TABBED_VIEW_SWITCHED, async (event) => { 238*90c8c64dSAndroid Build Coastguard Worker const newActiveTrace = event.newFocusedView.traces[0]; 239*90c8c64dSAndroid Build Coastguard Worker if (this.timelineData.trySetActiveTrace(newActiveTrace)) { 240*90c8c64dSAndroid Build Coastguard Worker const activeTraceChanged = new ActiveTraceChanged(newActiveTrace); 241*90c8c64dSAndroid Build Coastguard Worker await this.timelineComponent?.onWinscopeEvent(activeTraceChanged); 242*90c8c64dSAndroid Build Coastguard Worker for (const viewer of this.viewers) { 243*90c8c64dSAndroid Build Coastguard Worker await viewer.onWinscopeEvent(activeTraceChanged); 244*90c8c64dSAndroid Build Coastguard Worker } 245*90c8c64dSAndroid Build Coastguard Worker } 246*90c8c64dSAndroid Build Coastguard Worker this.focusedTabView = event.newFocusedView; 247*90c8c64dSAndroid Build Coastguard Worker await this.propagateTracePosition( 248*90c8c64dSAndroid Build Coastguard Worker this.timelineData.getCurrentPosition(), 249*90c8c64dSAndroid Build Coastguard Worker false, 250*90c8c64dSAndroid Build Coastguard Worker ); 251*90c8c64dSAndroid Build Coastguard Worker UserNotifier.notify(); 252*90c8c64dSAndroid Build Coastguard Worker }); 253*90c8c64dSAndroid Build Coastguard Worker 254*90c8c64dSAndroid Build Coastguard Worker await event.visit( 255*90c8c64dSAndroid Build Coastguard Worker WinscopeEventType.TRACE_POSITION_UPDATE, 256*90c8c64dSAndroid Build Coastguard Worker async (event) => { 257*90c8c64dSAndroid Build Coastguard Worker if (event.updateTimeline) { 258*90c8c64dSAndroid Build Coastguard Worker this.timelineData.setPosition(event.position); 259*90c8c64dSAndroid Build Coastguard Worker } 260*90c8c64dSAndroid Build Coastguard Worker await this.propagateTracePosition(event.position, false); 261*90c8c64dSAndroid Build Coastguard Worker UserNotifier.notify(); 262*90c8c64dSAndroid Build Coastguard Worker }, 263*90c8c64dSAndroid Build Coastguard Worker ); 264*90c8c64dSAndroid Build Coastguard Worker 265*90c8c64dSAndroid Build Coastguard Worker await event.visit( 266*90c8c64dSAndroid Build Coastguard Worker WinscopeEventType.EXPANDED_TIMELINE_TOGGLED, 267*90c8c64dSAndroid Build Coastguard Worker async (event) => { 268*90c8c64dSAndroid Build Coastguard Worker await this.propagateToOverlays(event); 269*90c8c64dSAndroid Build Coastguard Worker }, 270*90c8c64dSAndroid Build Coastguard Worker ); 271*90c8c64dSAndroid Build Coastguard Worker 272*90c8c64dSAndroid Build Coastguard Worker await event.visit(WinscopeEventType.ACTIVE_TRACE_CHANGED, async (event) => { 273*90c8c64dSAndroid Build Coastguard Worker this.timelineData.trySetActiveTrace(event.trace); 274*90c8c64dSAndroid Build Coastguard Worker for (const viewer of this.viewers) { 275*90c8c64dSAndroid Build Coastguard Worker await viewer.onWinscopeEvent(event); 276*90c8c64dSAndroid Build Coastguard Worker } 277*90c8c64dSAndroid Build Coastguard Worker await this.timelineComponent?.onWinscopeEvent(event); 278*90c8c64dSAndroid Build Coastguard Worker }); 279*90c8c64dSAndroid Build Coastguard Worker 280*90c8c64dSAndroid Build Coastguard Worker await event.visit(WinscopeEventType.DARK_MODE_TOGGLED, async (event) => { 281*90c8c64dSAndroid Build Coastguard Worker await this.timelineComponent?.onWinscopeEvent(event); 282*90c8c64dSAndroid Build Coastguard Worker for (const viewer of this.viewers) { 283*90c8c64dSAndroid Build Coastguard Worker await viewer.onWinscopeEvent(event); 284*90c8c64dSAndroid Build Coastguard Worker } 285*90c8c64dSAndroid Build Coastguard Worker }); 286*90c8c64dSAndroid Build Coastguard Worker 287*90c8c64dSAndroid Build Coastguard Worker await event.visit( 288*90c8c64dSAndroid Build Coastguard Worker WinscopeEventType.NO_TRACE_TARGETS_SELECTED, 289*90c8c64dSAndroid Build Coastguard Worker async (event) => { 290*90c8c64dSAndroid Build Coastguard Worker UserNotifier.add(new NoTraceTargetsSelected()).notify(); 291*90c8c64dSAndroid Build Coastguard Worker }, 292*90c8c64dSAndroid Build Coastguard Worker ); 293*90c8c64dSAndroid Build Coastguard Worker 294*90c8c64dSAndroid Build Coastguard Worker await event.visit( 295*90c8c64dSAndroid Build Coastguard Worker WinscopeEventType.FILTER_PRESET_SAVE_REQUEST, 296*90c8c64dSAndroid Build Coastguard Worker async (event) => { 297*90c8c64dSAndroid Build Coastguard Worker await this.findViewerByType(event.traceType)?.onWinscopeEvent(event); 298*90c8c64dSAndroid Build Coastguard Worker }, 299*90c8c64dSAndroid Build Coastguard Worker ); 300*90c8c64dSAndroid Build Coastguard Worker 301*90c8c64dSAndroid Build Coastguard Worker await event.visit( 302*90c8c64dSAndroid Build Coastguard Worker WinscopeEventType.FILTER_PRESET_APPLY_REQUEST, 303*90c8c64dSAndroid Build Coastguard Worker async (event) => { 304*90c8c64dSAndroid Build Coastguard Worker await this.findViewerByType(event.traceType)?.onWinscopeEvent(event); 305*90c8c64dSAndroid Build Coastguard Worker }, 306*90c8c64dSAndroid Build Coastguard Worker ); 307*90c8c64dSAndroid Build Coastguard Worker 308*90c8c64dSAndroid Build Coastguard Worker await event.visit(WinscopeEventType.TRACE_SEARCH_REQUEST, async (event) => { 309*90c8c64dSAndroid Build Coastguard Worker await this.timelineComponent?.onWinscopeEvent(event); 310*90c8c64dSAndroid Build Coastguard Worker const searchViewer = this.viewers.find( 311*90c8c64dSAndroid Build Coastguard Worker (viewer) => viewer.getViews()[0].type === ViewType.GLOBAL_SEARCH, 312*90c8c64dSAndroid Build Coastguard Worker ); 313*90c8c64dSAndroid Build Coastguard Worker const trace = await this.tracePipeline.tryCreateSearchTrace(event.query); 314*90c8c64dSAndroid Build Coastguard Worker this.timelineComponent?.onWinscopeEvent(new TraceSearchCompleted()); 315*90c8c64dSAndroid Build Coastguard Worker if (!trace) { 316*90c8c64dSAndroid Build Coastguard Worker await searchViewer?.onWinscopeEvent(new TraceSearchFailed()); 317*90c8c64dSAndroid Build Coastguard Worker return; 318*90c8c64dSAndroid Build Coastguard Worker } 319*90c8c64dSAndroid Build Coastguard Worker const newSearchTrace = new TraceAddRequest(trace); 320*90c8c64dSAndroid Build Coastguard Worker await searchViewer?.onWinscopeEvent(newSearchTrace); 321*90c8c64dSAndroid Build Coastguard Worker if (trace.lengthEntries > 0 && !trace.isDumpWithoutTimestamp()) { 322*90c8c64dSAndroid Build Coastguard Worker assertDefined(this.timelineData).getTraces().addTrace(trace); 323*90c8c64dSAndroid Build Coastguard Worker await this.timelineComponent?.onWinscopeEvent(newSearchTrace); 324*90c8c64dSAndroid Build Coastguard Worker } 325*90c8c64dSAndroid Build Coastguard Worker }); 326*90c8c64dSAndroid Build Coastguard Worker 327*90c8c64dSAndroid Build Coastguard Worker await event.visit(WinscopeEventType.TRACE_REMOVE_REQUEST, async (event) => { 328*90c8c64dSAndroid Build Coastguard Worker this.tracePipeline.getTraces().deleteTrace(event.trace); 329*90c8c64dSAndroid Build Coastguard Worker if (this.timelineData.hasTrace(event.trace)) { 330*90c8c64dSAndroid Build Coastguard Worker this.timelineData.getTraces().deleteTrace(event.trace); 331*90c8c64dSAndroid Build Coastguard Worker await this.timelineComponent?.onWinscopeEvent(event); 332*90c8c64dSAndroid Build Coastguard Worker } 333*90c8c64dSAndroid Build Coastguard Worker }); 334*90c8c64dSAndroid Build Coastguard Worker 335*90c8c64dSAndroid Build Coastguard Worker await event.visit( 336*90c8c64dSAndroid Build Coastguard Worker WinscopeEventType.INITIALIZE_TRACE_SEARCH_REQUEST, 337*90c8c64dSAndroid Build Coastguard Worker async (event) => { 338*90c8c64dSAndroid Build Coastguard Worker await this.timelineComponent?.onWinscopeEvent(event); 339*90c8c64dSAndroid Build Coastguard Worker const traces = this.tracePipeline.getTraces(); 340*90c8c64dSAndroid Build Coastguard Worker const views = await TraceSearchInitializer.createSearchViews(traces); 341*90c8c64dSAndroid Build Coastguard Worker const searchViewer = this.viewers.find( 342*90c8c64dSAndroid Build Coastguard Worker (viewer) => viewer.getViews()[0].type === ViewType.GLOBAL_SEARCH, 343*90c8c64dSAndroid Build Coastguard Worker ); 344*90c8c64dSAndroid Build Coastguard Worker const initializedEvent = new TraceSearchInitialized(views); 345*90c8c64dSAndroid Build Coastguard Worker await searchViewer?.onWinscopeEvent(initializedEvent); 346*90c8c64dSAndroid Build Coastguard Worker await this.timelineComponent?.onWinscopeEvent(initializedEvent); 347*90c8c64dSAndroid Build Coastguard Worker }, 348*90c8c64dSAndroid Build Coastguard Worker ); 349*90c8c64dSAndroid Build Coastguard Worker } 350*90c8c64dSAndroid Build Coastguard Worker 351*90c8c64dSAndroid Build Coastguard Worker private async loadFiles(files: File[], source: FilesSource) { 352*90c8c64dSAndroid Build Coastguard Worker await this.tracePipeline.loadFiles( 353*90c8c64dSAndroid Build Coastguard Worker files, 354*90c8c64dSAndroid Build Coastguard Worker source, 355*90c8c64dSAndroid Build Coastguard Worker this.currentProgressListener, 356*90c8c64dSAndroid Build Coastguard Worker ); 357*90c8c64dSAndroid Build Coastguard Worker } 358*90c8c64dSAndroid Build Coastguard Worker 359*90c8c64dSAndroid Build Coastguard Worker private async propagateTracePosition( 360*90c8c64dSAndroid Build Coastguard Worker position: TracePosition | undefined, 361*90c8c64dSAndroid Build Coastguard Worker omitCrossToolProtocol: boolean, 362*90c8c64dSAndroid Build Coastguard Worker ) { 363*90c8c64dSAndroid Build Coastguard Worker if (!position) { 364*90c8c64dSAndroid Build Coastguard Worker return; 365*90c8c64dSAndroid Build Coastguard Worker } 366*90c8c64dSAndroid Build Coastguard Worker 367*90c8c64dSAndroid Build Coastguard Worker const event = new TracePositionUpdate(position); 368*90c8c64dSAndroid Build Coastguard Worker const viewers: Viewer[] = [...this.viewers].filter((viewer) => 369*90c8c64dSAndroid Build Coastguard Worker this.isViewerVisible(viewer), 370*90c8c64dSAndroid Build Coastguard Worker ); 371*90c8c64dSAndroid Build Coastguard Worker 372*90c8c64dSAndroid Build Coastguard Worker const warnings: UserWarning[] = []; 373*90c8c64dSAndroid Build Coastguard Worker 374*90c8c64dSAndroid Build Coastguard Worker for (const viewer of viewers) { 375*90c8c64dSAndroid Build Coastguard Worker try { 376*90c8c64dSAndroid Build Coastguard Worker await viewer.onWinscopeEvent(event); 377*90c8c64dSAndroid Build Coastguard Worker } catch (e) { 378*90c8c64dSAndroid Build Coastguard Worker const traceType = assertDefined(viewer.getTraces().at(0)?.type); 379*90c8c64dSAndroid Build Coastguard Worker warnings.push( 380*90c8c64dSAndroid Build Coastguard Worker new CannotVisualizeTraceEntry( 381*90c8c64dSAndroid Build Coastguard Worker `Cannot parse entry for ${TRACE_INFO[traceType].name} trace: Trace may be corrupted.`, 382*90c8c64dSAndroid Build Coastguard Worker ), 383*90c8c64dSAndroid Build Coastguard Worker ); 384*90c8c64dSAndroid Build Coastguard Worker } 385*90c8c64dSAndroid Build Coastguard Worker } 386*90c8c64dSAndroid Build Coastguard Worker 387*90c8c64dSAndroid Build Coastguard Worker if (this.timelineComponent) { 388*90c8c64dSAndroid Build Coastguard Worker await this.timelineComponent.onWinscopeEvent(event); 389*90c8c64dSAndroid Build Coastguard Worker } 390*90c8c64dSAndroid Build Coastguard Worker 391*90c8c64dSAndroid Build Coastguard Worker if (!omitCrossToolProtocol) { 392*90c8c64dSAndroid Build Coastguard Worker await this.crossToolProtocol.onWinscopeEvent(event); 393*90c8c64dSAndroid Build Coastguard Worker } 394*90c8c64dSAndroid Build Coastguard Worker 395*90c8c64dSAndroid Build Coastguard Worker if (warnings.length > 0) { 396*90c8c64dSAndroid Build Coastguard Worker warnings.forEach((w) => UserNotifier.add(w)); 397*90c8c64dSAndroid Build Coastguard Worker } 398*90c8c64dSAndroid Build Coastguard Worker } 399*90c8c64dSAndroid Build Coastguard Worker 400*90c8c64dSAndroid Build Coastguard Worker private isViewerVisible(viewer: Viewer): boolean { 401*90c8c64dSAndroid Build Coastguard Worker if (!this.focusedTabView) { 402*90c8c64dSAndroid Build Coastguard Worker // During initialization no tab is focused. 403*90c8c64dSAndroid Build Coastguard Worker // Let's just consider all viewers as visible and to be updated. 404*90c8c64dSAndroid Build Coastguard Worker return true; 405*90c8c64dSAndroid Build Coastguard Worker } 406*90c8c64dSAndroid Build Coastguard Worker 407*90c8c64dSAndroid Build Coastguard Worker return viewer.getViews().some((view) => { 408*90c8c64dSAndroid Build Coastguard Worker if (view === this.focusedTabView) { 409*90c8c64dSAndroid Build Coastguard Worker return true; 410*90c8c64dSAndroid Build Coastguard Worker } 411*90c8c64dSAndroid Build Coastguard Worker if (view.type === ViewType.OVERLAY) { 412*90c8c64dSAndroid Build Coastguard Worker // Nice to have: update viewer only if overlay view is actually visible (not minimized) 413*90c8c64dSAndroid Build Coastguard Worker return true; 414*90c8c64dSAndroid Build Coastguard Worker } 415*90c8c64dSAndroid Build Coastguard Worker return false; 416*90c8c64dSAndroid Build Coastguard Worker }); 417*90c8c64dSAndroid Build Coastguard Worker } 418*90c8c64dSAndroid Build Coastguard Worker 419*90c8c64dSAndroid Build Coastguard Worker private async processRemoteToolDeferredTimestampReceived( 420*90c8c64dSAndroid Build Coastguard Worker deferredTimestamp: () => Timestamp | undefined, 421*90c8c64dSAndroid Build Coastguard Worker ) { 422*90c8c64dSAndroid Build Coastguard Worker this.lastRemoteToolDeferredTimestampReceived = deferredTimestamp; 423*90c8c64dSAndroid Build Coastguard Worker 424*90c8c64dSAndroid Build Coastguard Worker if (!this.areViewersLoaded) { 425*90c8c64dSAndroid Build Coastguard Worker return; // apply timestamp later when traces are visualized 426*90c8c64dSAndroid Build Coastguard Worker } 427*90c8c64dSAndroid Build Coastguard Worker 428*90c8c64dSAndroid Build Coastguard Worker const timestamp = deferredTimestamp(); 429*90c8c64dSAndroid Build Coastguard Worker if (!timestamp) { 430*90c8c64dSAndroid Build Coastguard Worker return; 431*90c8c64dSAndroid Build Coastguard Worker } 432*90c8c64dSAndroid Build Coastguard Worker 433*90c8c64dSAndroid Build Coastguard Worker const position = this.timelineData.makePositionFromActiveTrace(timestamp); 434*90c8c64dSAndroid Build Coastguard Worker this.timelineData.setPosition(position); 435*90c8c64dSAndroid Build Coastguard Worker 436*90c8c64dSAndroid Build Coastguard Worker await this.propagateTracePosition( 437*90c8c64dSAndroid Build Coastguard Worker this.timelineData.getCurrentPosition(), 438*90c8c64dSAndroid Build Coastguard Worker true, 439*90c8c64dSAndroid Build Coastguard Worker ); 440*90c8c64dSAndroid Build Coastguard Worker UserNotifier.notify(); 441*90c8c64dSAndroid Build Coastguard Worker } 442*90c8c64dSAndroid Build Coastguard Worker 443*90c8c64dSAndroid Build Coastguard Worker private async processRemoteFilesReceived(files: File[], source: FilesSource) { 444*90c8c64dSAndroid Build Coastguard Worker await this.resetAppToInitialState(); 445*90c8c64dSAndroid Build Coastguard Worker this.currentProgressListener = this.uploadTracesComponent; 446*90c8c64dSAndroid Build Coastguard Worker await this.loadFiles(files, source); 447*90c8c64dSAndroid Build Coastguard Worker UserNotifier.notify(); 448*90c8c64dSAndroid Build Coastguard Worker } 449*90c8c64dSAndroid Build Coastguard Worker 450*90c8c64dSAndroid Build Coastguard Worker private async loadViewers() { 451*90c8c64dSAndroid Build Coastguard Worker this.currentProgressListener?.onProgressUpdate( 452*90c8c64dSAndroid Build Coastguard Worker 'Computing frame mapping...', 453*90c8c64dSAndroid Build Coastguard Worker undefined, 454*90c8c64dSAndroid Build Coastguard Worker ); 455*90c8c64dSAndroid Build Coastguard Worker 456*90c8c64dSAndroid Build Coastguard Worker // TODO: move this into the ProgressListener 457*90c8c64dSAndroid Build Coastguard Worker // allow the UI to update before making the main thread very busy 458*90c8c64dSAndroid Build Coastguard Worker await TimeUtils.sleepMs(10); 459*90c8c64dSAndroid Build Coastguard Worker 460*90c8c64dSAndroid Build Coastguard Worker this.tracePipeline.filterTracesWithoutVisualization(); 461*90c8c64dSAndroid Build Coastguard Worker if (this.tracePipeline.getTraces().getSize() === 0) { 462*90c8c64dSAndroid Build Coastguard Worker this.currentProgressListener?.onOperationFinished(false); 463*90c8c64dSAndroid Build Coastguard Worker return; 464*90c8c64dSAndroid Build Coastguard Worker } 465*90c8c64dSAndroid Build Coastguard Worker 466*90c8c64dSAndroid Build Coastguard Worker try { 467*90c8c64dSAndroid Build Coastguard Worker await this.tracePipeline.buildTraces(); 468*90c8c64dSAndroid Build Coastguard Worker this.currentProgressListener?.onOperationFinished(true); 469*90c8c64dSAndroid Build Coastguard Worker } catch (e) { 470*90c8c64dSAndroid Build Coastguard Worker UserNotifier.add(new IncompleteFrameMapping((e as Error).message)); 471*90c8c64dSAndroid Build Coastguard Worker this.currentProgressListener?.onOperationFinished(false); 472*90c8c64dSAndroid Build Coastguard Worker } 473*90c8c64dSAndroid Build Coastguard Worker 474*90c8c64dSAndroid Build Coastguard Worker this.currentProgressListener?.onProgressUpdate( 475*90c8c64dSAndroid Build Coastguard Worker 'Initializing UI...', 476*90c8c64dSAndroid Build Coastguard Worker undefined, 477*90c8c64dSAndroid Build Coastguard Worker ); 478*90c8c64dSAndroid Build Coastguard Worker 479*90c8c64dSAndroid Build Coastguard Worker // TODO: move this into the ProgressListener 480*90c8c64dSAndroid Build Coastguard Worker // allow the UI to update before making the main thread very busy 481*90c8c64dSAndroid Build Coastguard Worker await TimeUtils.sleepMs(10); 482*90c8c64dSAndroid Build Coastguard Worker 483*90c8c64dSAndroid Build Coastguard Worker try { 484*90c8c64dSAndroid Build Coastguard Worker await this.timelineData.initialize( 485*90c8c64dSAndroid Build Coastguard Worker this.tracePipeline.getTraces(), 486*90c8c64dSAndroid Build Coastguard Worker await this.tracePipeline.getScreenRecordingVideo(), 487*90c8c64dSAndroid Build Coastguard Worker this.tracePipeline.getTimestampConverter(), 488*90c8c64dSAndroid Build Coastguard Worker ); 489*90c8c64dSAndroid Build Coastguard Worker } catch { 490*90c8c64dSAndroid Build Coastguard Worker this.currentProgressListener?.onOperationFinished(false); 491*90c8c64dSAndroid Build Coastguard Worker UserNotifier.add(new FailedToInitializeTimelineData()); 492*90c8c64dSAndroid Build Coastguard Worker return; 493*90c8c64dSAndroid Build Coastguard Worker } 494*90c8c64dSAndroid Build Coastguard Worker 495*90c8c64dSAndroid Build Coastguard Worker this.viewers = new ViewerFactory().createViewers( 496*90c8c64dSAndroid Build Coastguard Worker this.tracePipeline.getTraces(), 497*90c8c64dSAndroid Build Coastguard Worker this.storage, 498*90c8c64dSAndroid Build Coastguard Worker ); 499*90c8c64dSAndroid Build Coastguard Worker this.viewers.forEach((viewer) => 500*90c8c64dSAndroid Build Coastguard Worker viewer.setEmitEvent(async (event) => { 501*90c8c64dSAndroid Build Coastguard Worker await this.onWinscopeEvent(event); 502*90c8c64dSAndroid Build Coastguard Worker }), 503*90c8c64dSAndroid Build Coastguard Worker ); 504*90c8c64dSAndroid Build Coastguard Worker 505*90c8c64dSAndroid Build Coastguard Worker // Set initial trace position as soon as UI is created 506*90c8c64dSAndroid Build Coastguard Worker const initialPosition = this.getInitialTracePosition(); 507*90c8c64dSAndroid Build Coastguard Worker this.timelineData.setPosition(initialPosition); 508*90c8c64dSAndroid Build Coastguard Worker 509*90c8c64dSAndroid Build Coastguard Worker // Make sure all viewers are initialized and have performed the heavy pre-processing they need 510*90c8c64dSAndroid Build Coastguard Worker // at this stage, while the "initializing UI" progress message is still being displayed. 511*90c8c64dSAndroid Build Coastguard Worker // The viewers initialization is triggered by sending them a "trace position update". 512*90c8c64dSAndroid Build Coastguard Worker await this.propagateTracePosition(initialPosition, true); 513*90c8c64dSAndroid Build Coastguard Worker 514*90c8c64dSAndroid Build Coastguard Worker this.focusedTabView = this.viewers 515*90c8c64dSAndroid Build Coastguard Worker .find((v) => v.getViews()[0].type === ViewType.TRACE_TAB) 516*90c8c64dSAndroid Build Coastguard Worker ?.getViews()[0]; 517*90c8c64dSAndroid Build Coastguard Worker this.areViewersLoaded = true; 518*90c8c64dSAndroid Build Coastguard Worker 519*90c8c64dSAndroid Build Coastguard Worker // Notify app component (i.e. render viewers), only after all viewers have been initialized 520*90c8c64dSAndroid Build Coastguard Worker // (see above). 521*90c8c64dSAndroid Build Coastguard Worker // 522*90c8c64dSAndroid Build Coastguard Worker // Notifying the app component first could result in this kind of interleaved execution: 523*90c8c64dSAndroid Build Coastguard Worker // 1. Mediator notifies app component 524*90c8c64dSAndroid Build Coastguard Worker // 1.1. App component renders UI components 525*90c8c64dSAndroid Build Coastguard Worker // 1.2. Mediator receives back a "view switched" event 526*90c8c64dSAndroid Build Coastguard Worker // 1.2. Mediator sends "trace position update" to viewers 527*90c8c64dSAndroid Build Coastguard Worker // 2. Mediator sends "trace position update" to viewers to initialize them (see above) 528*90c8c64dSAndroid Build Coastguard Worker // 529*90c8c64dSAndroid Build Coastguard Worker // and because our data load operations are async and involve task suspensions, the two 530*90c8c64dSAndroid Build Coastguard Worker // "trace position update" could be processed concurrently within the same viewer. 531*90c8c64dSAndroid Build Coastguard Worker // Meaning the viewer could perform twice the initial heavy pre-processing, 532*90c8c64dSAndroid Build Coastguard Worker // thus increasing UI initialization times. 533*90c8c64dSAndroid Build Coastguard Worker await this.appComponent.onWinscopeEvent(new ViewersLoaded(this.viewers)); 534*90c8c64dSAndroid Build Coastguard Worker } 535*90c8c64dSAndroid Build Coastguard Worker 536*90c8c64dSAndroid Build Coastguard Worker private getInitialTracePosition(): TracePosition | undefined { 537*90c8c64dSAndroid Build Coastguard Worker if (this.lastRemoteToolDeferredTimestampReceived) { 538*90c8c64dSAndroid Build Coastguard Worker const lastRemoteToolTimestamp = 539*90c8c64dSAndroid Build Coastguard Worker this.lastRemoteToolDeferredTimestampReceived(); 540*90c8c64dSAndroid Build Coastguard Worker if (lastRemoteToolTimestamp) { 541*90c8c64dSAndroid Build Coastguard Worker return this.timelineData.makePositionFromActiveTrace( 542*90c8c64dSAndroid Build Coastguard Worker lastRemoteToolTimestamp, 543*90c8c64dSAndroid Build Coastguard Worker ); 544*90c8c64dSAndroid Build Coastguard Worker } 545*90c8c64dSAndroid Build Coastguard Worker } 546*90c8c64dSAndroid Build Coastguard Worker 547*90c8c64dSAndroid Build Coastguard Worker const position = this.timelineData.getCurrentPosition(); 548*90c8c64dSAndroid Build Coastguard Worker if (position) { 549*90c8c64dSAndroid Build Coastguard Worker return position; 550*90c8c64dSAndroid Build Coastguard Worker } 551*90c8c64dSAndroid Build Coastguard Worker 552*90c8c64dSAndroid Build Coastguard Worker // TimelineData might not provide a TracePosition because all the loaded traces are 553*90c8c64dSAndroid Build Coastguard Worker // dumps with invalid timestamps (value zero). In this case let's create a TracePosition 554*90c8c64dSAndroid Build Coastguard Worker // out of any entry from the loaded traces (if available). 555*90c8c64dSAndroid Build Coastguard Worker const firstEntries = this.tracePipeline 556*90c8c64dSAndroid Build Coastguard Worker .getTraces() 557*90c8c64dSAndroid Build Coastguard Worker .mapTrace((trace) => { 558*90c8c64dSAndroid Build Coastguard Worker if (trace.lengthEntries > 0) { 559*90c8c64dSAndroid Build Coastguard Worker return trace.getEntry(0); 560*90c8c64dSAndroid Build Coastguard Worker } 561*90c8c64dSAndroid Build Coastguard Worker return undefined; 562*90c8c64dSAndroid Build Coastguard Worker }) 563*90c8c64dSAndroid Build Coastguard Worker .filter((entry) => { 564*90c8c64dSAndroid Build Coastguard Worker return entry !== undefined; 565*90c8c64dSAndroid Build Coastguard Worker }) as Array<TraceEntry<object>>; 566*90c8c64dSAndroid Build Coastguard Worker 567*90c8c64dSAndroid Build Coastguard Worker if (firstEntries.length > 0) { 568*90c8c64dSAndroid Build Coastguard Worker return TracePosition.fromTraceEntry(firstEntries[0]); 569*90c8c64dSAndroid Build Coastguard Worker } 570*90c8c64dSAndroid Build Coastguard Worker 571*90c8c64dSAndroid Build Coastguard Worker return undefined; 572*90c8c64dSAndroid Build Coastguard Worker } 573*90c8c64dSAndroid Build Coastguard Worker 574*90c8c64dSAndroid Build Coastguard Worker private async resetAppToInitialState() { 575*90c8c64dSAndroid Build Coastguard Worker this.tracePipeline.clear(); 576*90c8c64dSAndroid Build Coastguard Worker this.timelineData.clear(); 577*90c8c64dSAndroid Build Coastguard Worker this.viewers = []; 578*90c8c64dSAndroid Build Coastguard Worker this.areViewersLoaded = false; 579*90c8c64dSAndroid Build Coastguard Worker this.lastRemoteToolDeferredTimestampReceived = undefined; 580*90c8c64dSAndroid Build Coastguard Worker this.focusedTabView = undefined; 581*90c8c64dSAndroid Build Coastguard Worker await this.appComponent.onWinscopeEvent(new ViewersUnloaded()); 582*90c8c64dSAndroid Build Coastguard Worker } 583*90c8c64dSAndroid Build Coastguard Worker 584*90c8c64dSAndroid Build Coastguard Worker private async propagateToOverlays(event: ExpandedTimelineToggled) { 585*90c8c64dSAndroid Build Coastguard Worker const overlayViewers = this.viewers.filter((viewer) => 586*90c8c64dSAndroid Build Coastguard Worker viewer.getViews().some((view) => view.type === ViewType.OVERLAY), 587*90c8c64dSAndroid Build Coastguard Worker ); 588*90c8c64dSAndroid Build Coastguard Worker for (const overlay of overlayViewers) { 589*90c8c64dSAndroid Build Coastguard Worker await overlay.onWinscopeEvent(event); 590*90c8c64dSAndroid Build Coastguard Worker } 591*90c8c64dSAndroid Build Coastguard Worker } 592*90c8c64dSAndroid Build Coastguard Worker 593*90c8c64dSAndroid Build Coastguard Worker private findViewerByType(type: TraceType): Viewer | undefined { 594*90c8c64dSAndroid Build Coastguard Worker return this.viewers.find((viewer) => viewer.getTraces()[0].type === type); 595*90c8c64dSAndroid Build Coastguard Worker } 596*90c8c64dSAndroid Build Coastguard Worker} 597