import*as Common from'../common/common.js';import*as SDK from'../sdk/sdk.js';import*as Workspace from'../workspace/workspace.js';import{FileSystem,FileSystemWorkspaceBinding}from'./FileSystemWorkspaceBinding.js';import{PersistenceBinding}from'./PersistenceImpl.js';export class NetworkPersistenceManager extends Common.ObjectWrapper.ObjectWrapper{constructor(workspace){super();this._bindingSymbol=Symbol('NetworkPersistenceBinding');this._originalResponseContentPromiseSymbol=Symbol('OriginalResponsePromise');this._savingSymbol=Symbol('SavingForOverrides');this._enabledSetting=self.Common.settings.moduleSetting('persistenceNetworkOverridesEnabled');this._enabledSetting.addChangeListener(this._enabledChanged,this);this._workspace=workspace;this._networkUISourceCodeForEncodedPath=new Map();this._interceptionHandlerBound=this._interceptionHandler.bind(this);this._updateInterceptionThrottler=new Common.Throttler.Throttler(50);this._project=null;this._activeProject=null;this._active=false;this._enabled=false;this._workspace.addEventListener(Workspace.Workspace.Events.ProjectAdded,event=>this._onProjectAdded((event.data)));this._workspace.addEventListener(Workspace.Workspace.Events.ProjectRemoved,event=>this._onProjectRemoved((event.data)));self.Persistence.persistence.addNetworkInterceptor(this._canHandleNetworkUISourceCode.bind(this));this._eventDescriptors=[];this._enabledChanged();}
active(){return this._active;}
project(){return this._project;}
originalContentForUISourceCode(uiSourceCode){if(!uiSourceCode[this._bindingSymbol]){return null;}
const fileSystemUISourceCode=uiSourceCode[this._bindingSymbol].fileSystem;return fileSystemUISourceCode[this._originalResponseContentPromiseSymbol]||null;}
async _enabledChanged(){if(this._enabled===this._enabledSetting.get()){return;}
this._enabled=this._enabledSetting.get();if(this._enabled){this._eventDescriptors=[self.Workspace.workspace.addEventListener(Workspace.Workspace.Events.UISourceCodeRenamed,async event=>{const uiSourceCode=(event.data.uiSourceCode);await this._onUISourceCodeRemoved(uiSourceCode);await this._onUISourceCodeAdded(uiSourceCode);}),self.Workspace.workspace.addEventListener(Workspace.Workspace.Events.UISourceCodeAdded,async event=>await this._onUISourceCodeAdded((event.data))),self.Workspace.workspace.addEventListener(Workspace.Workspace.Events.UISourceCodeRemoved,async event=>await this._onUISourceCodeRemoved((event.data))),self.Workspace.workspace.addEventListener(Workspace.Workspace.Events.WorkingCopyCommitted,event=>this._onUISourceCodeWorkingCopyCommitted((event.data.uiSourceCode)))];await this._updateActiveProject();}else{Common.EventTarget.EventTarget.removeEventListeners(this._eventDescriptors);await this._updateActiveProject();}}
async _updateActiveProject(){const wasActive=this._active;this._active=!!(this._enabledSetting.get()&&self.SDK.targetManager.mainTarget()&&this._project);if(this._active===wasActive){return;}
if(this._active){await Promise.all(this._project.uiSourceCodes().map(uiSourceCode=>this._filesystemUISourceCodeAdded(uiSourceCode)));const networkProjects=this._workspace.projectsForType(Workspace.Workspace.projectTypes.Network);for(const networkProject of networkProjects){await Promise.all(networkProject.uiSourceCodes().map(uiSourceCode=>this._networkUISourceCodeAdded(uiSourceCode)));}}else if(this._project){await Promise.all(this._project.uiSourceCodes().map(uiSourceCode=>this._filesystemUISourceCodeRemoved(uiSourceCode)));this._networkUISourceCodeForEncodedPath.clear();}
self.Persistence.persistence.refreshAutomapping();}
_encodedPathFromUrl(url){if(!this._active){return'';}
let urlPath=Common.ParsedURL.ParsedURL.urlWithoutHash(url.replace(/^https?:\/\//,''));if(urlPath.endsWith('/')&&urlPath.indexOf('?')===-1){urlPath=urlPath+'index.html';}
let encodedPathParts=encodeUrlPathToLocalPathParts(urlPath);const projectPath=FileSystemWorkspaceBinding.fileSystemPath(this._project.id());const encodedPath=encodedPathParts.join('/');if(projectPath.length+encodedPath.length>200){const domain=encodedPathParts[0];const encodedFileName=encodedPathParts[encodedPathParts.length-1];const shortFileName=encodedFileName?encodedFileName.substr(0,10)+'-':'';const extension=Common.ParsedURL.ParsedURL.extractExtension(urlPath);const extensionPart=extension?'.'+extension.substr(0,10):'';encodedPathParts=[domain,'longurls',shortFileName+String.hashCode(encodedPath).toString(16)+extensionPart];}
return encodedPathParts.join('/');function encodeUrlPathToLocalPathParts(urlPath){const encodedParts=[];for(const pathPart of fileNamePartsFromUrlPath(urlPath)){if(!pathPart){continue;}
let encodedName=encodeURI(pathPart).replace(/[\/:\?\*]/g,match=>'%'+match[0].charCodeAt(0).toString(16));if(_reservedFileNames.has(encodedName.toLowerCase())){encodedName=encodedName.split('').map(char=>'%'+char.charCodeAt(0).toString(16)).join('');}
const lastChar=encodedName.charAt(encodedName.length-1);if(lastChar==='.'){encodedName=encodedName.substr(0,encodedName.length-1)+'%2e';}
encodedParts.push(encodedName);}
return encodedParts;}
function fileNamePartsFromUrlPath(urlPath){urlPath=Common.ParsedURL.ParsedURL.urlWithoutHash(urlPath);const queryIndex=urlPath.indexOf('?');if(queryIndex===-1){return urlPath.split('/');}
if(queryIndex===0){return[urlPath];}
const endSection=urlPath.substr(queryIndex);const parts=urlPath.substr(0,urlPath.length-endSection.length).split('/');parts[parts.length-1]+=endSection;return parts;}}
_decodeLocalPathToUrlPath(path){try{return unescape(path);}catch(e){console.error(e);}
return path;}
async _unbind(uiSourceCode){const binding=uiSourceCode[this._bindingSymbol];if(binding){delete binding.network[this._bindingSymbol];delete binding.fileSystem[this._bindingSymbol];await self.Persistence.persistence.removeBinding(binding);}}
async _bind(networkUISourceCode,fileSystemUISourceCode){if(networkUISourceCode[this._bindingSymbol]){await this._unbind(networkUISourceCode);}
if(fileSystemUISourceCode[this._bindingSymbol]){await this._unbind(fileSystemUISourceCode);}
const binding=new PersistenceBinding(networkUISourceCode,fileSystemUISourceCode);networkUISourceCode[this._bindingSymbol]=binding;fileSystemUISourceCode[this._bindingSymbol]=binding;await self.Persistence.persistence.addBinding(binding);const uiSourceCodeOfTruth=networkUISourceCode[this._savingSymbol]?networkUISourceCode:fileSystemUISourceCode;const[{content},encoded]=await Promise.all([uiSourceCodeOfTruth.requestContent(),uiSourceCodeOfTruth.contentEncoded()]);self.Persistence.persistence.syncContent(uiSourceCodeOfTruth,content,encoded);}
_onUISourceCodeWorkingCopyCommitted(uiSourceCode){this.saveUISourceCodeForOverrides(uiSourceCode);}
canSaveUISourceCodeForOverrides(uiSourceCode){return this._active&&uiSourceCode.project().type()===Workspace.Workspace.projectTypes.Network&&!uiSourceCode[this._bindingSymbol]&&!uiSourceCode[this._savingSymbol];}
async saveUISourceCodeForOverrides(uiSourceCode){if(!this.canSaveUISourceCodeForOverrides(uiSourceCode)){return;}
uiSourceCode[this._savingSymbol]=true;let encodedPath=this._encodedPathFromUrl(uiSourceCode.url());const content=(await uiSourceCode.requestContent()).content||'';const encoded=await uiSourceCode.contentEncoded();const lastIndexOfSlash=encodedPath.lastIndexOf('/');const encodedFileName=encodedPath.substr(lastIndexOfSlash+1);encodedPath=encodedPath.substr(0,lastIndexOfSlash);await this._project.createFile(encodedPath,encodedFileName,content,encoded);this._fileCreatedForTest(encodedPath,encodedFileName);uiSourceCode[this._savingSymbol]=false;}
_fileCreatedForTest(path,fileName){}
_patternForFileSystemUISourceCode(uiSourceCode){const relativePathParts=FileSystemWorkspaceBinding.relativePath(uiSourceCode);if(relativePathParts.length<2){return'';}
if(relativePathParts[1]==='longurls'&&relativePathParts.length!==2){return'http?://'+relativePathParts[0]+'/*';}
return'http?://'+this._decodeLocalPathToUrlPath(relativePathParts.join('/'));}
async _onUISourceCodeAdded(uiSourceCode){await this._networkUISourceCodeAdded(uiSourceCode);await this._filesystemUISourceCodeAdded(uiSourceCode);}
_canHandleNetworkUISourceCode(uiSourceCode){return this._active&&!uiSourceCode.url().startsWith('snippet://');}
async _networkUISourceCodeAdded(uiSourceCode){if(uiSourceCode.project().type()!==Workspace.Workspace.projectTypes.Network||!this._canHandleNetworkUISourceCode(uiSourceCode)){return;}
const url=Common.ParsedURL.ParsedURL.urlWithoutHash(uiSourceCode.url());this._networkUISourceCodeForEncodedPath.set(this._encodedPathFromUrl(url),uiSourceCode);const fileSystemUISourceCode=this._project.uiSourceCodeForURL((this._project).fileSystemPath()+'/'+this._encodedPathFromUrl(url));if(fileSystemUISourceCode){await this._bind(uiSourceCode,fileSystemUISourceCode);}}
async _filesystemUISourceCodeAdded(uiSourceCode){if(!this._active||uiSourceCode.project()!==this._project){return;}
this._updateInterceptionPatterns();const relativePath=FileSystemWorkspaceBinding.relativePath(uiSourceCode);const networkUISourceCode=this._networkUISourceCodeForEncodedPath.get(relativePath.join('/'));if(networkUISourceCode){await this._bind(networkUISourceCode,uiSourceCode);}}
_updateInterceptionPatterns(){this._updateInterceptionThrottler.schedule(innerUpdateInterceptionPatterns.bind(this));function innerUpdateInterceptionPatterns(){if(!this._active){return self.SDK.multitargetNetworkManager.setInterceptionHandlerForPatterns([],this._interceptionHandlerBound);}
const patterns=new Set();const indexFileName='index.html';for(const uiSourceCode of this._project.uiSourceCodes()){const pattern=this._patternForFileSystemUISourceCode(uiSourceCode);patterns.add(pattern);if(pattern.endsWith('/'+indexFileName)){patterns.add(pattern.substr(0,pattern.length-indexFileName.length));}}
return self.SDK.multitargetNetworkManager.setInterceptionHandlerForPatterns(Array.from(patterns).map(pattern=>({urlPattern:pattern,interceptionStage:Protocol.Network.InterceptionStage.HeadersReceived})),this._interceptionHandlerBound);}}
async _onUISourceCodeRemoved(uiSourceCode){await this._networkUISourceCodeRemoved(uiSourceCode);await this._filesystemUISourceCodeRemoved(uiSourceCode);}
async _networkUISourceCodeRemoved(uiSourceCode){if(uiSourceCode.project().type()===Workspace.Workspace.projectTypes.Network){await this._unbind(uiSourceCode);this._networkUISourceCodeForEncodedPath.delete(this._encodedPathFromUrl(uiSourceCode.url()));}}
async _filesystemUISourceCodeRemoved(uiSourceCode){if(uiSourceCode.project()!==this._project){return;}
this._updateInterceptionPatterns();delete uiSourceCode[this._originalResponseContentPromiseSymbol];await this._unbind(uiSourceCode);}
async _setProject(project){if(project===this._project){return;}
if(this._project){await Promise.all(this._project.uiSourceCodes().map(uiSourceCode=>this._filesystemUISourceCodeRemoved(uiSourceCode)));}
this._project=project;if(this._project){await Promise.all(this._project.uiSourceCodes().map(uiSourceCode=>this._filesystemUISourceCodeAdded(uiSourceCode)));}
await this._updateActiveProject();this.dispatchEventToListeners(Events.ProjectChanged,this._project);}
async _onProjectAdded(project){if(project.type()!==Workspace.Workspace.projectTypes.FileSystem||FileSystemWorkspaceBinding.fileSystemType(project)!=='overrides'){return;}
const fileSystemPath=FileSystemWorkspaceBinding.fileSystemPath(project.id());if(!fileSystemPath){return;}
if(this._project){this._project.remove();}
await this._setProject(project);}
async _onProjectRemoved(project){if(project===this._project){await this._setProject(null);}}
async _interceptionHandler(interceptedRequest){const method=interceptedRequest.request.method;if(!this._active||(method!=='GET'&&method!=='POST')){return;}
const path=(this._project).fileSystemPath()+'/'+
this._encodedPathFromUrl(interceptedRequest.request.url);const fileSystemUISourceCode=this._project.uiSourceCodeForURL(path);if(!fileSystemUISourceCode){return;}
let mimeType='';if(interceptedRequest.responseHeaders){const responseHeaders=SDK.NetworkManager.NetworkManager.lowercaseHeaders(interceptedRequest.responseHeaders);mimeType=responseHeaders['content-type'];}
if(!mimeType){const expectedResourceType=Common.ResourceType.resourceTypes[interceptedRequest.resourceType]||Common.ResourceType.resourceTypes.Other;mimeType=fileSystemUISourceCode.mimeType();if(Common.ResourceType.ResourceType.fromMimeType(mimeType)!==expectedResourceType){mimeType=expectedResourceType.canonicalMimeType();}}
const project=(fileSystemUISourceCode.project());fileSystemUISourceCode[this._originalResponseContentPromiseSymbol]=interceptedRequest.responseBody().then(response=>{if(response.error||response.content===null){return null;}
return response.encoded?atob(response.content):response.content;});const blob=await project.requestFileBlob(fileSystemUISourceCode);interceptedRequest.continueRequestWithContent(new Blob([blob],{type:mimeType}));}}
const _reservedFileNames=new Set(['con','prn','aux','nul','com1','com2','com3','com4','com5','com6','com7','com8','com9','lpt1','lpt2','lpt3','lpt4','lpt5','lpt6','lpt7','lpt8','lpt9']);export const Events={ProjectChanged:Symbol('ProjectChanged')};