import*as childProcess from"child_process";import*as readline from"readline";import removeFolder from"rimraf";import{promisify}from"util";import{assert}from"../common/assert.js";import{Connection}from"../common/Connection.js";import{debug}from"../common/Debug.js";import{TimeoutError}from"../common/Errors.js";import{debugError,helper}from"../common/helper.js";import{NodeWebSocketTransport as WebSocketTransport}from"../node/NodeWebSocketTransport.js";import{PipeTransport}from"./PipeTransport.js";const removeFolderAsync=promisify(removeFolder),debugLauncher=debug("puppeteer:launcher"),PROCESS_ERROR_EXPLANATION="Puppeteer was unable to kill the process which ran the browser binary.\nThis means that, on future Puppeteer launches, Puppeteer might not be able to launch the browser.\nPlease check your open processes and ensure that the browser processes that Puppeteer launched have been killed.\nIf you think this is a bug, please report it on the Puppeteer issue tracker.";export class BrowserRunner{constructor(e,t,r){this.proc=null,this.connection=null,this._closed=!0,this._listeners=[],this._executablePath=e,this._processArguments=t,this._tempDirectory=r}start(e){const{handleSIGINT:t,handleSIGTERM:r,handleSIGHUP:s,dumpio:o,env:i,pipe:n}=e;let p=["pipe","pipe","pipe"];n&&(p=o?["ignore","pipe","pipe","pipe","pipe"]:["ignore","ignore","ignore","pipe","pipe"]),assert(!this.proc,"This process has previously been started."),debugLauncher(`Calling ${this._executablePath} ${this._processArguments.join(" ")}`),this.proc=childProcess.spawn(this._executablePath,this._processArguments,{detached:"win32"!==process.platform,env:i,stdio:p}),o&&(this.proc.stderr.pipe(process.stderr),this.proc.stdout.pipe(process.stdout)),this._closed=!1,this._processClosing=new Promise((e=>{this.proc.once("exit",(()=>{this._closed=!0,this._tempDirectory?removeFolderAsync(this._tempDirectory).then((()=>e())).catch((e=>console.error(e))):e()}))})),this._listeners=[helper.addEventListener(process,"exit",this.kill.bind(this))],t&&this._listeners.push(helper.addEventListener(process,"SIGINT",(()=>{this.kill(),process.exit(130)}))),r&&this._listeners.push(helper.addEventListener(process,"SIGTERM",this.close.bind(this))),s&&this._listeners.push(helper.addEventListener(process,"SIGHUP",this.close.bind(this)))}close(){return this._closed?Promise.resolve():(this._tempDirectory?this.kill():this.connection&&this.connection.send("Browser.close").catch((e=>{debugError(e),this.kill()})),helper.removeEventListeners(this._listeners),this._processClosing)}kill(){try{removeFolder.sync(this._tempDirectory)}catch(e){}if(this.proc&&this.proc.pid&&!this.proc.killed)try{this.proc.kill("SIGKILL")}catch(e){throw new Error(`${PROCESS_ERROR_EXPLANATION}\nError cause: ${e.stack}`)}helper.removeEventListeners(this._listeners)}async setupConnection(e){const{usePipe:t,timeout:r,slowMo:s,preferredRevision:o}=e;if(t){const{3:e,4:t}=this.proc.stdio,r=new PipeTransport(e,t);this.connection=new Connection("",r,s)}else{const e=await waitForWSEndpoint(this.proc,r,o),t=await WebSocketTransport.create(e);this.connection=new Connection(e,t,s)}return this.connection}}function waitForWSEndpoint(e,t,r){return new Promise(((s,o)=>{const i=readline.createInterface({input:e.stderr});let n="";const p=[helper.addEventListener(i,"line",(function(e){n+=e+"\n";const t=e.match(/^DevTools listening on (ws:\/\/.*)$/);if(!t)return;l(),s(t[1])})),helper.addEventListener(i,"close",(()=>h())),helper.addEventListener(e,"exit",(()=>h())),helper.addEventListener(e,"error",(e=>h(e)))],c=t?setTimeout((function(){l(),o(new TimeoutError(`Timed out after ${t} ms while trying to connect to the browser! Only Chrome at revision r${r} is guaranteed to work.`))}),t):0;function h(e){l(),o(new Error(["Failed to launch the browser process!"+(e?" "+e.message:""),n,"","TROUBLESHOOTING: https://github.com/puppeteer/puppeteer/blob/main/docs/troubleshooting.md",""].join("\n")))}function l(){c&&clearTimeout(c),helper.removeEventListeners(p)}}))}