import{SDKModel,TracingManager,TracingModel}from'../sdk/sdk.js';import{FileUtils,TempFile}from'../bindings/bindings.js';import{InspectorBackend}from'../protocol_client/protocol_client.js';import{TimelineLoader}from'../timeline/timeline.js';import{Widget,Toolbar,UIUtils,ViewManager}from'../ui/ui.js';class InputModel extends SDKModel.SDKModel{constructor(target){super(target);this._inputAgent=target.inputAgent();this._eventDispatchTimer=null;this._dispatchEventDataList=[];this._finishCallback=null;this._reset();}
_reset(){this._lastEventTime=null;this._replayPaused=false;this._dispatchingIndex=0;clearTimeout(this._eventDispatchTimer);}
setEvents(tracingModel){this._dispatchEventDataList=[];for(const process of tracingModel.sortedProcesses()){for(const thread of process.sortedThreads()){this._processThreadEvents(tracingModel,thread);}}
function compareTimestamp(a,b){return a.timestamp-b.timestamp;}
this._dispatchEventDataList.sort(compareTimestamp);}
startReplay(finishCallback){this._reset();this._finishCallback=finishCallback;if(this._dispatchEventDataList.length){this._dispatchNextEvent();}else{this._replayStopped();}}
pause(){clearTimeout(this._eventDispatchTimer);if(this._dispatchingIndex>=this._dispatchEventDataList.length){this._replayStopped();}else{this._replayPaused=true;}}
resume(){this._replayPaused=false;if(this._dispatchingIndex<this._dispatchEventDataList.length){this._dispatchNextEvent();}}
_processThreadEvents(tracingModel,thread){for(const event of thread.events()){if(event.name==='EventDispatch'&&this._isValidInputEvent(event.args.data)){this._dispatchEventDataList.push(event.args.data);}}}
_isValidInputEvent(eventData){return this._isMouseEvent((eventData))||this._isKeyboardEvent((eventData));}
_isMouseEvent(eventData){if(!InputModel.MouseEventTypes.has(eventData.type)){return false;}
if(!('x'in eventData&&'y'in eventData)){return false;}
return true;}
_isKeyboardEvent(eventData){if(!InputModel.KeyboardEventTypes.has(eventData.type)){return false;}
if(!('code'in eventData&&'key'in eventData)){return false;}
return true;}
_dispatchNextEvent(){const eventData=this._dispatchEventDataList[this._dispatchingIndex];this._lastEventTime=eventData.timestamp;if(InputModel.MouseEventTypes.has(eventData.type)){this._dispatchMouseEvent((eventData));}else if(InputModel.KeyboardEventTypes.has(eventData.type)){this._dispatchKeyEvent((eventData));}
++this._dispatchingIndex;if(this._dispatchingIndex<this._dispatchEventDataList.length){const waitTime=(this._dispatchEventDataList[this._dispatchingIndex].timestamp-this._lastEventTime)/1000;this._eventDispatchTimer=setTimeout(this._dispatchNextEvent.bind(this),waitTime);}else{this._replayStopped();}}
async _dispatchMouseEvent(eventData){console.assert(InputModel.MouseEventTypes.has(eventData.type));const buttons={0:'left',1:'middle',2:'right',3:'back',4:'forward'};const params={type:InputModel.MouseEventTypes.get(eventData.type),x:eventData.x,y:eventData.y,modifiers:eventData.modifiers,button:(eventData.type==='mousedown'||eventData.type==='mouseup')?buttons[eventData.button]:'none',buttons:eventData.buttons,clickCount:eventData.clickCount,deltaX:eventData.deltaX,deltaY:eventData.deltaY};await this._inputAgent.invoke_dispatchMouseEvent(params);}
async _dispatchKeyEvent(eventData){console.assert(InputModel.KeyboardEventTypes.has(eventData.type));const text=eventData.type==='keypress'?eventData.key[0]:undefined;const params={type:InputModel.KeyboardEventTypes.get(eventData.type),modifiers:eventData.modifiers,text:text,unmodifiedText:text?text.toLowerCase():undefined,code:eventData.code,key:eventData.key};await this._inputAgent.invoke_dispatchKeyEvent(params);}
_replayStopped(){clearTimeout(this._eventDispatchTimer);this._reset();this._finishCallback();}}
InputModel.MouseEventTypes=new Map([['mousedown','mousePressed'],['mouseup','mouseReleased'],['mousemove','mouseMoved'],['wheel','mouseWheel']]);InputModel.KeyboardEventTypes=new Map([['keydown','keyDown'],['keyup','keyUp'],['keypress','char']]);SDKModel.SDKModel.register(InputModel,SDKModel.Capability.Input,false);let EventData;let MouseEventData;let KeyboardEventData;var InputModel$1=Object.freeze({__proto__:null,InputModel:InputModel,EventData:EventData,MouseEventData:MouseEventData,KeyboardEventData:KeyboardEventData});class InputTimeline extends Widget.VBox{constructor(){super(true);this.registerRequiredCSS('input/inputTimeline.css');this.element.classList.add('inputs-timeline');this._tracingClient=null;this._tracingModel=null;this._inputModel=null;this._state=State.Idle;this._toggleRecordAction=(self.UI.actionRegistry.action('input.toggle-recording'));this._startReplayAction=(self.UI.actionRegistry.action('input.start-replaying'));this._togglePauseAction=(self.UI.actionRegistry.action('input.toggle-pause'));const toolbarContainer=this.contentElement.createChild('div','input-timeline-toolbar-container');this._panelToolbar=new Toolbar.Toolbar('input-timeline-toolbar',toolbarContainer);this._panelToolbar.appendToolbarItem(Toolbar.Toolbar.createActionButton(this._toggleRecordAction));this._panelToolbar.appendToolbarItem(Toolbar.Toolbar.createActionButton(this._startReplayAction));this._panelToolbar.appendToolbarItem(Toolbar.Toolbar.createActionButton(this._togglePauseAction));this._clearButton=new Toolbar.ToolbarButton(ls`Clear all`,'largeicon-clear');this._clearButton.addEventListener(Toolbar.ToolbarButton.Events.Click,this._reset.bind(this));this._panelToolbar.appendToolbarItem(this._clearButton);this._panelToolbar.appendSeparator();this._loadButton=new Toolbar.ToolbarButton(Common.UIString('Load profile…'),'largeicon-load');this._loadButton.addEventListener(Toolbar.ToolbarButton.Events.Click,()=>this._selectFileToLoad());this._saveButton=new Toolbar.ToolbarButton(Common.UIString('Save profile…'),'largeicon-download');this._saveButton.addEventListener(Toolbar.ToolbarButton.Events.Click,event=>{this._saveToFile();});this._panelToolbar.appendSeparator();this._panelToolbar.appendToolbarItem(this._loadButton);this._panelToolbar.appendToolbarItem(this._saveButton);this._panelToolbar.appendSeparator();this._createFileSelector();this._updateControls();}
_reset(){this._tracingClient=null;this._tracingModel=null;this._inputModel=null;this._setState(State.Idle);}
_createFileSelector(){if(this._fileSelectorElement){this._fileSelectorElement.remove();}
this._fileSelectorElement=UIUtils.createFileSelectorElement(this._loadFromFile.bind(this));this.element.appendChild(this._fileSelectorElement);}
wasShown(){}
willHide(){}
_setState(state){this._state=state;this._updateControls();}
_isAvailableState(){return this._state===State.Idle||this._state===State.ReplayPaused;}
_updateControls(){this._toggleRecordAction.setToggled(this._state===State.Recording);this._toggleRecordAction.setEnabled(this._isAvailableState()||this._state===State.Recording);this._startReplayAction.setEnabled(this._isAvailableState()&&!!this._tracingModel);this._togglePauseAction.setEnabled(this._state===State.Replaying||this._state===State.ReplayPaused);this._togglePauseAction.setToggled(this._state===State.ReplayPaused);this._clearButton.setEnabled(this._isAvailableState());this._loadButton.setEnabled(this._isAvailableState());this._saveButton.setEnabled(this._isAvailableState()&&!!this._tracingModel);}
_toggleRecording(){switch(this._state){case State.Recording:{this._stopRecording();break;}
case State.Idle:{this._startRecording();break;}}}
_startReplay(){this._replayEvents();}
_toggleReplayPause(){switch(this._state){case State.Replaying:{this._pauseReplay();break;}
case State.ReplayPaused:{this._resumeReplay();break;}}}
async _saveToFile(){console.assert(this._state===State.Idle&&this._tracingModel);const fileName=`InputProfile-${new Date().toISO8601Compact()}.json`;const stream=new FileUtils.FileOutputStream();const accepted=await stream.open(fileName);if(!accepted){return;}
const backingStorage=(this._tracingModel.backingStorage());await backingStorage.writeToStream(stream);stream.close();}
_selectFileToLoad(){this._fileSelectorElement.click();}
_loadFromFile(file){console.assert(this._isAvailableState());this._setState(State.Loading);this._loader=TimelineLoader.TimelineLoader.loadFromFile(file,this);this._createFileSelector();}
async _startRecording(){this._setState(State.StartPending);this._tracingClient=new InputTimeline.TracingClient((SDKModel.TargetManager.instance().mainTarget()),this);const response=await this._tracingClient.startRecording();if(response[InspectorBackend.ProtocolError]){this._recordingFailed(response[InspectorBackend.ProtocolError]);}else{this._setState(State.Recording);}}
async _stopRecording(){this._setState(State.StopPending);await this._tracingClient.stopRecording();this._tracingClient=null;}
async _replayEvents(){this._setState(State.Replaying);await this._inputModel.startReplay(this.replayStopped.bind(this));}
_pauseReplay(){this._inputModel.pause();this._setState(State.ReplayPaused);}
_resumeReplay(){this._inputModel.resume();this._setState(State.Replaying);}
loadingStarted(){}
loadingProgress(progress){}
processingStarted(){}
loadingComplete(tracingModel){if(!tracingModel){this._reset();return;}
this._inputModel=new InputModel((SDKModel.TargetManager.instance().mainTarget()));this._tracingModel=tracingModel;this._inputModel.setEvents(tracingModel);this._setState(State.Idle);}
_recordingFailed(error){this._tracingClient=null;this._setState(State.Idle);}
replayStopped(){this._setState(State.Idle);}}
const State={Idle:Symbol('Idle'),StartPending:Symbol('StartPending'),Recording:Symbol('Recording'),StopPending:Symbol('StopPending'),Replaying:Symbol('Replaying'),ReplayPaused:Symbol('ReplayPaused'),Loading:Symbol('Loading')};class ActionDelegate{handleAction(context,actionId){const inputViewId='Inputs';ViewManager.ViewManager.instance().showView(inputViewId).then(()=>ViewManager.ViewManager.instance().view(inputViewId).widget()).then(widget=>this._innerHandleAction((widget),actionId));return true;}
_innerHandleAction(inputTimeline,actionId){switch(actionId){case'input.toggle-recording':inputTimeline._toggleRecording();break;case'input.start-replaying':inputTimeline._startReplay();break;case'input.toggle-pause':inputTimeline._toggleReplayPause();break;default:console.assert(false,`Unknown action: ${actionId}`);}}}
class TracingClient{constructor(target,client){this._target=target;this._tracingManager=target.model(TracingManager.TracingManager);this._client=client;const backingStorage=new TempFile.TempFileBackingStorage();this._tracingModel=new TracingModel.TracingModel(backingStorage);this._tracingCompleteCallback=null;}
async startRecording(){function disabledByDefault(category){return'disabled-by-default-'+category;}
const categoriesArray=['devtools.timeline',disabledByDefault('devtools.timeline.inputs')];const categories=categoriesArray.join(',');const response=await this._tracingManager.start(this,categories,'');if(response['Protocol.Error']){await this._waitForTracingToStop(false);}
return response;}
async stopRecording(){if(this._tracingManager){this._tracingManager.stop();}
await this._waitForTracingToStop(true);await SDKModel.TargetManager.instance().resumeAllTargets();this._tracingModel.tracingComplete();this._client.loadingComplete(this._tracingModel);}
traceEventsCollected(events){this._tracingModel.addEvents(events);}
tracingComplete(){this._tracingCompleteCallback();this._tracingCompleteCallback=null;}
tracingBufferUsage(usage){}
eventsRetrievalProgress(progress){}
_waitForTracingToStop(awaitTracingCompleteCallback){return new Promise(resolve=>{if(this._tracingManager&&awaitTracingCompleteCallback){this._tracingCompleteCallback=resolve;}else{resolve();}});}}
var InputTimeline$1=Object.freeze({__proto__:null,InputTimeline:InputTimeline,State:State,ActionDelegate:ActionDelegate,TracingClient:TracingClient});export{InputModel$1 as InputModel,InputTimeline$1 as InputTimeline};