export class CoverageView extends UI.VBox{constructor(){super(true);this._model=null;this._decorationManager=null;this._resourceTreeModel=null;this.registerRequiredCSS('coverage/coverageView.css');const toolbarContainer=this.contentElement.createChild('div','coverage-toolbar-container');const toolbar=new UI.Toolbar('coverage-toolbar',toolbarContainer);this._coverageType=null;this._coverageTypeComboBox=new UI.ToolbarComboBox(null,ls`Choose coverage granularity: Per function has low overhead, per block has significant overhead.`);const coverageTypes=[{label:ls`Per function`,value:Coverage.CoverageType.JavaScript|Coverage.CoverageType.JavaScriptPerFunction,},{label:ls`Per block`,value:Coverage.CoverageType.JavaScript,},];for(const type of coverageTypes){this._coverageTypeComboBox.addOption(this._coverageTypeComboBox.createOption(type.label,type.value));}
this._coverageTypeComboBox.setSelectedIndex(0);this._coverageTypeComboBox.setEnabled(true);toolbar.appendToolbarItem(this._coverageTypeComboBox);this._toggleRecordAction=(UI.actionRegistry.action('coverage.toggle-recording'));this._toggleRecordButton=UI.Toolbar.createActionButton(this._toggleRecordAction);toolbar.appendToolbarItem(this._toggleRecordButton);const mainTarget=SDK.targetManager.mainTarget();const mainTargetSupportsRecordOnReload=mainTarget&&mainTarget.model(SDK.ResourceTreeModel);if(mainTargetSupportsRecordOnReload){const startWithReloadAction=(UI.actionRegistry.action('coverage.start-with-reload'));this._startWithReloadButton=UI.Toolbar.createActionButton(startWithReloadAction);toolbar.appendToolbarItem(this._startWithReloadButton);this._toggleRecordButton.setEnabled(false);this._toggleRecordButton.setVisible(false);}
this._clearButton=new UI.ToolbarButton(Common.UIString('Clear all'),'largeicon-clear');this._clearButton.addEventListener(UI.ToolbarButton.Events.Click,this._clear.bind(this));toolbar.appendToolbarItem(this._clearButton);toolbar.appendSeparator();const saveButton=new UI.ToolbarButton(Common.UIString('Export...'),'largeicon-download');saveButton.addEventListener(UI.ToolbarButton.Events.Click,()=>this._exportReport());toolbar.appendToolbarItem(saveButton);this._textFilterRegExp=null;toolbar.appendSeparator();this._filterInput=new UI.ToolbarInput(Common.UIString('URL filter'),'',0.4,1);this._filterInput.setEnabled(false);this._filterInput.addEventListener(UI.ToolbarInput.Event.TextChanged,this._onFilterChanged,this);toolbar.appendToolbarItem(this._filterInput);toolbar.appendSeparator();this._typeFilterValue=null;this._filterByTypeComboBox=new UI.ToolbarComboBox(this._onFilterByTypeChanged.bind(this),ls`Filter coverage by type`);const options=[{label:ls`All`,value:'',},{label:ls`CSS`,value:Coverage.CoverageType.CSS,},{label:ls`JavaScript`,value:Coverage.CoverageType.JavaScript|Coverage.CoverageType.JavaScriptPerFunction,},];for(const option of options){this._filterByTypeComboBox.addOption(this._filterByTypeComboBox.createOption(option.label,option.value));}
this._filterByTypeComboBox.setSelectedIndex(0);this._filterByTypeComboBox.setEnabled(false);toolbar.appendToolbarItem(this._filterByTypeComboBox);toolbar.appendSeparator();this._showContentScriptsSetting=Common.settings.createSetting('showContentScripts',false);this._showContentScriptsSetting.addChangeListener(this._onFilterChanged,this);const contentScriptsCheckbox=new UI.ToolbarSettingCheckbox(this._showContentScriptsSetting,Common.UIString('Include extension content scripts'),Common.UIString('Content scripts'));toolbar.appendToolbarItem(contentScriptsCheckbox);this._coverageResultsElement=this.contentElement.createChild('div','coverage-results');this._landingPage=this._buildLandingPage();this._listView=new Coverage.CoverageListView(this._isVisible.bind(this,false));this._statusToolbarElement=this.contentElement.createChild('div','coverage-toolbar-summary');this._statusMessageElement=this._statusToolbarElement.createChild('div','coverage-message');this._landingPage.show(this._coverageResultsElement);}
_buildLandingPage(){const widget=new UI.VBox();let message;if(this._startWithReloadButton){const reloadButton=UI.createInlineButton(UI.Toolbar.createActionButtonForId('coverage.start-with-reload'));message=UI.formatLocalized('Click the reload button %s to reload and start capturing coverage.',[reloadButton]);}else{const recordButton=UI.createInlineButton(UI.Toolbar.createActionButton(this._toggleRecordAction));message=UI.formatLocalized('Click the record button %s to start capturing coverage.',[recordButton]);}
message.classList.add('message');widget.contentElement.appendChild(message);widget.element.classList.add('landing-page');return widget;}
_clear(){if(this._model){this._model.reset();}
this._reset();}
_reset(){if(this._decorationManager){this._decorationManager.dispose();this._decorationManager=null;}
this._listView.reset();this._listView.detach();this._landingPage.show(this._coverageResultsElement);this._statusMessageElement.textContent='';this._filterInput.setEnabled(false);this._filterByTypeComboBox.setEnabled(false);}
_toggleRecording(){const enable=!this._toggleRecordAction.toggled();if(enable){this._startRecording({reload:false,jsCoveragePerBlock:this.isBlockCoverageSelected()});}else{this.stopRecording();}}
isBlockCoverageSelected(){const coverageType=Number(this._coverageTypeComboBox.selectedOption().value);return coverageType===Coverage.CoverageType.JavaScript;}
_selectCoverageType(jsCoveragePerBlock){const selectedIndex=jsCoveragePerBlock?1:0;this._coverageTypeComboBox.setSelectedIndex(selectedIndex);}
async ensureRecordingStarted(){const enabled=this._toggleRecordAction.toggled();if(enabled){await this.stopRecording();}
await this._startRecording({reload:false,jsCoveragePerBlock:false});}
async _startRecording(options){this._reset();const mainTarget=SDK.targetManager.mainTarget();if(!mainTarget){return;}
const{reload,jsCoveragePerBlock}={reload:false,jsCoveragePerBlock:false,...options};if(!this._model||reload){this._model=mainTarget.model(Coverage.CoverageModel);}
Host.userMetrics.actionTaken(Host.UserMetrics.Action.CoverageStarted);if(jsCoveragePerBlock){Host.userMetrics.actionTaken(Host.UserMetrics.Action.CoverageStartedPerBlock);}
const success=await this._model.start(jsCoveragePerBlock);if(!success){return;}
this._selectCoverageType(jsCoveragePerBlock);this._model.addEventListener(Coverage.CoverageModel.Events.CoverageUpdated,this._onCoverageDataReceived,this);this._resourceTreeModel=(mainTarget.model(SDK.ResourceTreeModel));if(this._resourceTreeModel){this._resourceTreeModel.addEventListener(SDK.ResourceTreeModel.Events.MainFrameNavigated,this._onMainFrameNavigated,this);}
this._decorationManager=new Coverage.CoverageDecorationManager((this._model));this._toggleRecordAction.setToggled(true);this._clearButton.setEnabled(false);if(this._startWithReloadButton){this._startWithReloadButton.setEnabled(false);this._startWithReloadButton.setVisible(false);this._toggleRecordButton.setEnabled(true);this._toggleRecordButton.setVisible(true);}
this._coverageTypeComboBox.setEnabled(false);this._filterInput.setEnabled(true);this._filterByTypeComboBox.setEnabled(true);if(this._landingPage.isShowing()){this._landingPage.detach();}
this._listView.show(this._coverageResultsElement);if(reload&&this._resourceTreeModel){this._resourceTreeModel.reloadPage();}else{this._model.startPolling();}}
_onCoverageDataReceived(event){this._updateViews(event.data);}
async stopRecording(){if(this._resourceTreeModel){this._resourceTreeModel.removeEventListener(SDK.ResourceTreeModel.Events.MainFrameNavigated,this._onMainFrameNavigated,this);this._resourceTreeModel=null;}
await this._model.stop();this._model.removeEventListener(Coverage.CoverageModel.Events.CoverageUpdated,this._onCoverageDataReceived,this);this._toggleRecordAction.setToggled(false);this._coverageTypeComboBox.setEnabled(true);if(this._startWithReloadButton){this._startWithReloadButton.setEnabled(true);this._startWithReloadButton.setVisible(true);this._toggleRecordButton.setEnabled(false);this._toggleRecordButton.setVisible(false);}
this._clearButton.setEnabled(true);}
_onMainFrameNavigated(){this._model.reset();this._decorationManager.reset();this._listView.reset();this._model.startPolling();}
_updateViews(updatedEntries){this._updateStats();this._listView.update(this._model.entries());this._decorationManager.update(updatedEntries);}
_updateStats(){let total=0;let unused=0;for(const info of this._model.entries()){if(!this._isVisible(true,info)){continue;}
total+=info.size();unused+=info.unusedSize();}
const used=total-unused;const percentUsed=total?Math.round(100*used/total):0;this._statusMessageElement.textContent=ls`${Number.bytesToString(used)} of ${Number.bytesToString(total)} (${percentUsed}%) used so far.
        ${Number.bytesToString(unused)} unused.`;}
_onFilterChanged(){if(!this._listView){return;}
const text=this._filterInput.value();this._textFilterRegExp=text?createPlainTextSearchRegex(text,'i'):null;this._listView.updateFilterAndHighlight(this._textFilterRegExp);this._updateStats();}
_onFilterByTypeChanged(){if(!this._listView){return;}
Host.userMetrics.actionTaken(Host.UserMetrics.Action.CoverageReportFiltered);const type=this._filterByTypeComboBox.selectedOption().value;this._typeFilterValue=parseInt(type,10)||null;this._listView.updateFilterAndHighlight(this._textFilterRegExp);this._updateStats();}
_isVisible(ignoreTextFilter,coverageInfo){const url=coverageInfo.url();if(url.startsWith(Coverage.CoverageView._extensionBindingsURLPrefix)){return false;}
if(coverageInfo.isContentScript()&&!this._showContentScriptsSetting.get()){return false;}
if(this._typeFilterValue&&!(coverageInfo.type()&this._typeFilterValue)){return false;}
return ignoreTextFilter||!this._textFilterRegExp||this._textFilterRegExp.test(url);}
async _exportReport(){const fos=new Bindings.FileOutputStream();const fileName=`Coverage-${new Date().toISO8601Compact()}.json`;const accepted=await fos.open(fileName);if(!accepted){return;}
this._model.exportReport(fos);}
selectCoverageItemByUrl(url){this._listView.selectByUrl(url);}}
CoverageView._extensionBindingsURLPrefix='extensions::';export class ActionDelegate{handleAction(context,actionId){const coverageViewId='coverage';UI.viewManager.showView(coverageViewId).then(()=>UI.viewManager.view(coverageViewId).widget()).then(widget=>this._innerHandleAction((widget),actionId));return true;}
_innerHandleAction(coverageView,actionId){switch(actionId){case'coverage.toggle-recording':coverageView._toggleRecording();break;case'coverage.start-with-reload':coverageView._startRecording({reload:true,jsCoveragePerBlock:coverageView.isBlockCoverageSelected()});break;default:console.assert(false,`Unknown action: ${actionId}`);}}}
export class LineDecorator{constructor(){this._listeners=new WeakMap();}
decorate(uiSourceCode,textEditor){const decorations=uiSourceCode.decorationsForType(Coverage.CoverageDecorationManager.decoratorType);if(!decorations||!decorations.size){this._uninstallGutter(textEditor);return;}
const decorationManager=(decorations.values().next().value.data());decorationManager.usageByLine(uiSourceCode).then(lineUsage=>{textEditor.operation(()=>this._innerDecorate(uiSourceCode,textEditor,lineUsage));});}
_innerDecorate(uiSourceCode,textEditor,lineUsage){const gutterType=Coverage.CoverageView.LineDecorator._gutterType;this._uninstallGutter(textEditor);if(lineUsage.length){this._installGutter(textEditor,uiSourceCode.url());}
for(let line=0;line<lineUsage.length;++line){if(typeof lineUsage[line]!=='boolean'){continue;}
const className=lineUsage[line]?'text-editor-coverage-used-marker':'text-editor-coverage-unused-marker';const gutterElement=createElementWithClass('div',className);textEditor.setGutterDecoration(line,gutterType,gutterElement);}}
makeGutterClickHandler(url){function handleGutterClick(event){const eventData=(event.data);if(eventData.gutterType!==Coverage.CoverageView.LineDecorator._gutterType){return;}
const coverageViewId='coverage';UI.viewManager.showView(coverageViewId).then(()=>UI.viewManager.view(coverageViewId).widget()).then(widget=>{const matchFormattedSuffix=url.match(/(.*):formatted$/);const urlWithoutFormattedSuffix=(matchFormattedSuffix&&matchFormattedSuffix[1])||url;widget.selectCoverageItemByUrl(urlWithoutFormattedSuffix);});}
return handleGutterClick;}
_installGutter(textEditor,url){let listener=this._listeners.get(textEditor);if(!listener){listener=this.makeGutterClickHandler(url);this._listeners.set(textEditor,listener);}
textEditor.installGutter(Coverage.CoverageView.LineDecorator._gutterType,false);textEditor.addEventListener(SourceFrame.SourcesTextEditor.Events.GutterClick,listener,this);}
_uninstallGutter(textEditor){textEditor.uninstallGutter(Coverage.CoverageView.LineDecorator._gutterType);const listener=this._listeners.get(textEditor);if(listener){textEditor.removeEventListener(SourceFrame.SourcesTextEditor.Events.GutterClick,listener,this);this._listeners.delete(textEditor);}}}
LineDecorator._gutterType='CodeMirror-gutter-coverage';self.Coverage=self.Coverage||{};Coverage=Coverage||{};Coverage.CoverageView=CoverageView;Coverage.CoverageView.LineDecorator=LineDecorator;Coverage.CoverageView.ActionDelegate=ActionDelegate;