import*as Common from'../common/common.js';import*as SDK from'../sdk/sdk.js';import{RecordType}from'./TimelineModel.js';export class TimelineIRModel{constructor(){this.reset();}
static phaseForEvent(event){return event[TimelineIRModel._eventIRPhase];}
populate(inputLatencies,animations){this.reset();if(!inputLatencies){return;}
this._processInputLatencies(inputLatencies);if(animations){this._processAnimations(animations);}
const range=new Common.SegmentedRange.SegmentedRange();range.appendRange(this._drags);range.appendRange(this._cssAnimations);range.appendRange(this._scrolls);range.appendRange(this._responses);this._segments=range.segments();}
_processInputLatencies(events){const eventTypes=InputEvents;const phases=Phases;const thresholdsMs=TimelineIRModel._mergeThresholdsMs;let scrollStart;let flingStart;let touchStart;let firstTouchMove;let mouseWheel;let mouseDown;let mouseMove;for(let i=0;i<events.length;++i){const event=events[i];if(i>0&&events[i].startTime<events[i-1].startTime){console.assert(false,'Unordered input events');}
const type=this._inputEventType(event.name);switch(type){case eventTypes.ScrollBegin:this._scrolls.append(this._segmentForEvent(event,phases.Scroll));scrollStart=event;break;case eventTypes.ScrollEnd:if(scrollStart){this._scrolls.append(this._segmentForEventRange(scrollStart,event,phases.Scroll));}else{this._scrolls.append(this._segmentForEvent(event,phases.Scroll));}
scrollStart=null;break;case eventTypes.ScrollUpdate:touchStart=null;this._scrolls.append(this._segmentForEvent(event,phases.Scroll));break;case eventTypes.FlingStart:if(flingStart){self.Common.console.error(Common.UIString.UIString('Two flings at the same time? %s vs %s',flingStart.startTime,event.startTime));break;}
flingStart=event;break;case eventTypes.FlingCancel:if(!flingStart){break;}
this._scrolls.append(this._segmentForEventRange(flingStart,event,phases.Fling));flingStart=null;break;case eventTypes.ImplSideFling:this._scrolls.append(this._segmentForEvent(event,phases.Fling));break;case eventTypes.ShowPress:case eventTypes.Tap:case eventTypes.KeyDown:case eventTypes.KeyDownRaw:case eventTypes.KeyUp:case eventTypes.Char:case eventTypes.Click:case eventTypes.ContextMenu:this._responses.append(this._segmentForEvent(event,phases.Response));break;case eventTypes.TouchStart:if(touchStart){self.Common.console.error(Common.UIString.UIString('Two touches at the same time? %s vs %s',touchStart.startTime,event.startTime));break;}
touchStart=event;event.steps[0][TimelineIRModel._eventIRPhase]=phases.Response;firstTouchMove=null;break;case eventTypes.TouchCancel:touchStart=null;break;case eventTypes.TouchMove:if(firstTouchMove){this._drags.append(this._segmentForEvent(event,phases.Drag));}else if(touchStart){firstTouchMove=event;this._responses.append(this._segmentForEventRange(touchStart,event,phases.Response));}
break;case eventTypes.TouchEnd:touchStart=null;break;case eventTypes.MouseDown:mouseDown=event;mouseMove=null;break;case eventTypes.MouseMove:if(mouseDown&&!mouseMove&&mouseDown.startTime+thresholdsMs.mouse>event.startTime){this._responses.append(this._segmentForEvent(mouseDown,phases.Response));this._responses.append(this._segmentForEvent(event,phases.Response));}else if(mouseDown){this._drags.append(this._segmentForEvent(event,phases.Drag));}
mouseMove=event;break;case eventTypes.MouseUp:this._responses.append(this._segmentForEvent(event,phases.Response));mouseDown=null;break;case eventTypes.MouseWheel:if(mouseWheel&&canMerge(thresholdsMs.mouse,mouseWheel,event)){this._scrolls.append(this._segmentForEventRange(mouseWheel,event,phases.Scroll));}else{this._scrolls.append(this._segmentForEvent(event,phases.Scroll));}
mouseWheel=event;break;}}
function canMerge(threshold,first,second){return first.endTime<second.startTime&&second.startTime<first.endTime+threshold;}}
_processAnimations(events){for(let i=0;i<events.length;++i){this._cssAnimations.append(this._segmentForEvent(events[i],Phases.Animation));}}
_segmentForEvent(event,phase){this._setPhaseForEvent(event,phase);return new Common.SegmentedRange.Segment(event.startTime,event.endTime,phase);}
_segmentForEventRange(startEvent,endEvent,phase){this._setPhaseForEvent(startEvent,phase);this._setPhaseForEvent(endEvent,phase);return new Common.SegmentedRange.Segment(startEvent.startTime,endEvent.endTime,phase);}
_setPhaseForEvent(asyncEvent,phase){asyncEvent.steps[0][TimelineIRModel._eventIRPhase]=phase;}
interactionRecords(){return this._segments;}
reset(){const thresholdsMs=TimelineIRModel._mergeThresholdsMs;this._segments=[];this._drags=new Common.SegmentedRange.SegmentedRange(merge.bind(null,thresholdsMs.mouse));this._cssAnimations=new Common.SegmentedRange.SegmentedRange(merge.bind(null,thresholdsMs.animation));this._responses=new Common.SegmentedRange.SegmentedRange(merge.bind(null,0));this._scrolls=new Common.SegmentedRange.SegmentedRange(merge.bind(null,thresholdsMs.animation));function merge(threshold,first,second){return first.end+threshold>=second.begin&&first.data===second.data?first:null;}}
_inputEventType(eventName){const prefix='InputLatency::';if(!eventName.startsWith(prefix)){if(eventName===InputEvents.ImplSideFling){return(eventName);}
console.error('Unrecognized input latency event: '+eventName);return null;}
return(eventName.substr(prefix.length));}}
export const Phases={Idle:'Idle',Response:'Response',Scroll:'Scroll',Fling:'Fling',Drag:'Drag',Animation:'Animation',Uncategorized:'Uncategorized'};export const InputEvents={Char:'Char',Click:'GestureClick',ContextMenu:'ContextMenu',FlingCancel:'GestureFlingCancel',FlingStart:'GestureFlingStart',ImplSideFling:RecordType.ImplSideFling,KeyDown:'KeyDown',KeyDownRaw:'RawKeyDown',KeyUp:'KeyUp',LatencyScrollUpdate:'ScrollUpdate',MouseDown:'MouseDown',MouseMove:'MouseMove',MouseUp:'MouseUp',MouseWheel:'MouseWheel',PinchBegin:'GesturePinchBegin',PinchEnd:'GesturePinchEnd',PinchUpdate:'GesturePinchUpdate',ScrollBegin:'GestureScrollBegin',ScrollEnd:'GestureScrollEnd',ScrollUpdate:'GestureScrollUpdate',ScrollUpdateRenderer:'ScrollUpdate',ShowPress:'GestureShowPress',Tap:'GestureTap',TapCancel:'GestureTapCancel',TapDown:'GestureTapDown',TouchCancel:'TouchCancel',TouchEnd:'TouchEnd',TouchMove:'TouchMove',TouchStart:'TouchStart'};TimelineIRModel._mergeThresholdsMs={animation:1,mouse:40,};TimelineIRModel._eventIRPhase=Symbol('eventIRPhase');