import{PerformanceModel}from'./PerformanceModel.js';import{TimelineEventOverviewCPUActivity,TimelineEventOverviewFrames,TimelineEventOverviewNetwork,TimelineEventOverviewResponsiveness,}from'./TimelineEventOverview.js';export class TimelineHistoryManager{constructor(){this._recordings=[];this._action=(self.UI.actionRegistry.action('timeline.show-history'));this._nextNumberByDomain=new Map();this._button=new ToolbarButton(this._action);UI.ARIAUtils.markAsMenuButton(this._button.element);this.clear();this._allOverviews=[{constructor:TimelineEventOverviewResponsiveness,height:3},{constructor:TimelineEventOverviewFrames,height:16},{constructor:TimelineEventOverviewCPUActivity,height:20},{constructor:TimelineEventOverviewNetwork,height:8}];this._totalHeight=this._allOverviews.reduce((acc,entry)=>acc+entry.height,0);this._enabled=true;this._lastActiveModel=null;}
addRecording(performanceModel){this._lastActiveModel=performanceModel;this._recordings.unshift(performanceModel);this._buildPreview(performanceModel);const modelTitle=this._title(performanceModel);this._button.setText(modelTitle);const buttonTitle=this._action.title();UI.ARIAUtils.setAccessibleName(this._button.element,ls`Current Session: ${modelTitle}. ${buttonTitle}`);this._updateState();if(this._recordings.length<=maxRecordings){return;}
const lruModel=this._recordings.reduce((a,b)=>lastUsedTime(a)<lastUsedTime(b)?a:b);this._recordings.splice(this._recordings.indexOf(lruModel),1);lruModel.dispose();function lastUsedTime(model){return TimelineHistoryManager._dataForModel(model).lastUsed;}}
setEnabled(enabled){this._enabled=enabled;this._updateState();}
button(){return this._button;}
clear(){this._recordings.forEach(model=>model.dispose());this._recordings=[];this._lastActiveModel=null;this._updateState();this._button.setText(Common.UIString('(no recordings)'));this._nextNumberByDomain.clear();}
async showHistoryDropDown(){if(this._recordings.length<2||!this._enabled){return null;}
const model=await DropDown.show(this._recordings,(this._lastActiveModel),this._button.element);if(!model){return null;}
const index=this._recordings.indexOf(model);if(index<0){console.assert(false,`selected recording not found`);return null;}
this._setCurrentModel(model);return model;}
cancelIfShowing(){DropDown.cancelIfShowing();}
navigate(direction){if(!this._enabled||!this._lastActiveModel){return null;}
const index=this._recordings.indexOf(this._lastActiveModel);if(index<0){return null;}
const newIndex=Number.constrain(index+direction,0,this._recordings.length-1);const model=this._recordings[newIndex];this._setCurrentModel(model);return model;}
_setCurrentModel(model){TimelineHistoryManager._dataForModel(model).lastUsed=Date.now();this._lastActiveModel=model;const modelTitle=this._title(model);const buttonTitle=this._action.title();this._button.setText(modelTitle);UI.ARIAUtils.setAccessibleName(this._button.element,ls`Current Session: ${modelTitle}. ${buttonTitle}`);}
_updateState(){this._action.setEnabled(this._recordings.length>1&&this._enabled);}
static _previewElement(performanceModel){const data=TimelineHistoryManager._dataForModel(performanceModel);const startedAt=performanceModel.recordStartTime();data.time.textContent=startedAt?Common.UIString('(%s ago)',TimelineHistoryManager._coarseAge(startedAt)):'';return data.preview;}
static _coarseAge(time){const seconds=Math.round((Date.now()-time)/1000);if(seconds<50){return Common.UIString('moments');}
const minutes=Math.round(seconds/60);if(minutes<50){return Common.UIString('%s m',minutes);}
const hours=Math.round(minutes/60);return Common.UIString('%s h',hours);}
_title(performanceModel){return TimelineHistoryManager._dataForModel(performanceModel).title;}
_buildPreview(performanceModel){const parsedURL=Common.ParsedURL.fromString(performanceModel.timelineModel().pageURL());const domain=parsedURL?parsedURL.host:'';const sequenceNumber=this._nextNumberByDomain.get(domain)||1;const title=Common.UIString('%s #%d',domain,sequenceNumber);this._nextNumberByDomain.set(domain,sequenceNumber+1);const timeElement=createElement('span');const preview=createElementWithClass('div','preview-item vbox');const data={preview:preview,title:title,time:timeElement,lastUsed:Date.now()};performanceModel[previewDataSymbol]=data;preview.appendChild(this._buildTextDetails(performanceModel,title,timeElement));const screenshotAndOverview=preview.createChild('div','hbox');screenshotAndOverview.appendChild(this._buildScreenshotThumbnail(performanceModel));screenshotAndOverview.appendChild(this._buildOverview(performanceModel));return data.preview;}
_buildTextDetails(performanceModel,title,timeElement){const container=createElementWithClass('div','text-details hbox');const nameSpan=container.createChild('span','name');nameSpan.textContent=title;UI.ARIAUtils.setAccessibleName(nameSpan,title);const tracingModel=performanceModel.tracingModel();const duration=Number.millisToString(tracingModel.maximumRecordTime()-tracingModel.minimumRecordTime(),false);const timeContainer=container.createChild('span','time');timeContainer.appendChild(createTextNode(duration));timeContainer.appendChild(timeElement);return container;}
_buildScreenshotThumbnail(performanceModel){const container=createElementWithClass('div','screenshot-thumb');const thumbnailAspectRatio=3/2;container.style.width=this._totalHeight*thumbnailAspectRatio+'px';container.style.height=this._totalHeight+'px';const filmStripModel=performanceModel.filmStripModel();const lastFrame=filmStripModel.frames().peekLast();if(!lastFrame){return container;}
lastFrame.imageDataPromise().then(data=>UI.loadImageFromData(data)).then(image=>image&&container.appendChild(image));return container;}
_buildOverview(performanceModel){const container=createElement('div');container.style.width=previewWidth+'px';container.style.height=this._totalHeight+'px';const canvas=container.createChild('canvas');canvas.width=window.devicePixelRatio*previewWidth;canvas.height=window.devicePixelRatio*this._totalHeight;const ctx=canvas.getContext('2d');let yOffset=0;for(const overview of this._allOverviews){const timelineOverview=new overview.constructor();timelineOverview.setCanvasSize(previewWidth,overview.height);timelineOverview.setModel(performanceModel);timelineOverview.update();const sourceContext=timelineOverview.context();const imageData=sourceContext.getImageData(0,0,sourceContext.canvas.width,sourceContext.canvas.height);ctx.putImageData(imageData,0,yOffset);yOffset+=overview.height*window.devicePixelRatio;}
return container;}
static _dataForModel(model){return model[previewDataSymbol]||null;}}
export const maxRecordings=5;export const previewWidth=450;export const previewDataSymbol=Symbol('previewData');export class DropDown{constructor(models){this._glassPane=new UI.GlassPane();this._glassPane.setSizeBehavior(UI.GlassPane.SizeBehavior.MeasureContent);this._glassPane.setOutsideClickCallback(()=>this._close(null));this._glassPane.setPointerEventsBehavior(UI.GlassPane.PointerEventsBehavior.BlockedByGlassPane);this._glassPane.setAnchorBehavior(UI.GlassPane.AnchorBehavior.PreferBottom);this._glassPane.element.addEventListener('blur',()=>this._close(null));const shadowRoot=UI.createShadowRootWithCoreStyles(this._glassPane.contentElement,'timeline/timelineHistoryManager.css');const contentElement=shadowRoot.createChild('div','drop-down');const listModel=new UI.ListModel();this._listControl=new UI.ListControl(listModel,this,UI.ListMode.NonViewport);this._listControl.element.addEventListener('mousemove',this._onMouseMove.bind(this),false);listModel.replaceAll(models);UI.ARIAUtils.markAsMenu(this._listControl.element);UI.ARIAUtils.setAccessibleName(this._listControl.element,ls`Select Timeline Session`);contentElement.appendChild(this._listControl.element);contentElement.addEventListener('keydown',this._onKeyDown.bind(this),false);contentElement.addEventListener('click',this._onClick.bind(this),false);this._focusRestorer=new UI.ElementFocusRestorer(this._listControl.element);this._selectionDone=null;}
static show(models,currentModel,anchor){if(DropDown._instance){return Promise.resolve((null));}
const instance=new DropDown(models);return instance._show(anchor,currentModel);}
static cancelIfShowing(){if(!DropDown._instance){return;}
DropDown._instance._close(null);}
_show(anchor,currentModel){DropDown._instance=this;this._glassPane.setContentAnchorBox(anchor.boxInWindow());this._glassPane.show((this._glassPane.contentElement.ownerDocument));this._listControl.element.focus();this._listControl.selectItem(currentModel);return new Promise(fulfill=>this._selectionDone=fulfill);}
_onMouseMove(event){const node=event.target.enclosingNodeOrSelfWithClass('preview-item');const listItem=node&&this._listControl.itemForNode(node);if(!listItem){return;}
this._listControl.selectItem(listItem);}
_onClick(event){if(!event.target.enclosingNodeOrSelfWithClass('preview-item')){return;}
this._close(this._listControl.selectedItem());}
_onKeyDown(event){switch(event.key){case'Tab':case'Escape':this._close(null);break;case'Enter':this._close(this._listControl.selectedItem());break;default:return;}
event.consume(true);}
_close(model){this._selectionDone(model);this._focusRestorer.restore();this._glassPane.hide();DropDown._instance=null;}
createElementForItem(item){const element=TimelineHistoryManager._previewElement(item);UI.ARIAUtils.markAsMenuItem(element);element.classList.remove('selected');return element;}
heightForItem(item){console.assert(false,'Should not be called');return 0;}
isItemSelectable(item){return true;}
selectedItemChanged(from,to,fromElement,toElement){if(fromElement){fromElement.classList.remove('selected');}
if(toElement){toElement.classList.add('selected');}}
updateSelectedItemARIA(fromElement,toElement){return false;}}
DropDown._instance=null;export class ToolbarButton extends UI.ToolbarItem{constructor(action){super(createElementWithClass('button','history-dropdown-button'));UI.appendStyle(this.element,'timeline/historyToolbarButton.css');this._contentElement=this.element.createChild('span','content');const dropdownArrowIcon=UI.Icon.create('smallicon-triangle-down');this.element.appendChild(dropdownArrowIcon);this.element.addEventListener('click',()=>void action.execute(),false);this.setEnabled(action.enabled());action.addEventListener(UI.Action.Events.Enabled,event=>this.setEnabled((event.data)));this.setTitle(action.title());}
setText(text){this._contentElement.textContent=text;}}