Files
orca/mobile/scripts/hosted-ios-webcontent-process.mjs
T

51 lines
1.6 KiB
JavaScript

import { execFile } from 'node:child_process'
import process from 'node:process'
import { promisify } from 'node:util'
const execFileAsync = promisify(execFile)
const webContentExecutable = '/WebContentExtension.appex/com.apple.WebKit.WebContent'
export async function terminateHostedIosWebContent(
deviceUdid,
runCommand = execFileAsync,
sendSignal = process.kill
) {
const manager = await runCommand(
'xcrun',
['simctl', 'spawn', deviceUdid, 'launchctl', 'managerpid'],
commandOptions()
)
const managerPid = Number.parseInt(manager.stdout.trim(), 10)
if (!Number.isSafeInteger(managerPid) || managerPid < 1) {
throw new Error('iOS Simulator launchd PID is unavailable')
}
const processes = await runCommand('ps', ['-axo', 'pid=,ppid=,command='], commandOptions())
const webContentPid = selectHostedIosWebContentPid(processes.stdout, managerPid)
sendSignal(webContentPid, 'SIGKILL')
return webContentPid
}
export function selectHostedIosWebContentPid(processTable, managerPid) {
const candidates = processTable.split(/\r?\n/u).flatMap((line) => {
const match = line.match(/^\s*(\d+)\s+(\d+)\s+(.+)$/u)
if (!match) {
return []
}
const pid = Number.parseInt(match[1], 10)
const parentPid = Number.parseInt(match[2], 10)
return parentPid === managerPid && match[3].includes(webContentExecutable) ? [pid] : []
})
if (candidates.length !== 1) {
throw new Error(`Expected one iOS WebContent process, found ${candidates.length}`)
}
return candidates[0]
}
function commandOptions() {
return {
encoding: 'utf8',
maxBuffer: 2 * 1024 * 1024,
timeout: 30_000
}
}