Files
Web_FreeCAD_Bitbybit/public/occt-browser-harness.html

106 lines
6.1 KiB
HTML

<!doctype html>
<meta charset="utf-8">
<title>OCCT Browser Harness</title>
<pre id="result">running</pre>
<script type="module">
const output = document.querySelector('#result')
let lastWorkerEvent = null
const MiB = 1024 * 1024
const memoryBudgets = { wasmPeakBytes: 256 * MiB, wasmRetainedBytes: 16 * MiB, browserRetainedBytes: 64 * MiB, gpuLiveResources: 0 }
const measureBrowserMemory = async () => {
if (typeof performance.measureUserAgentSpecificMemory === 'function') {
try {
const measurement = await performance.measureUserAgentSpecificMemory()
return { api: 'measureUserAgentSpecificMemory', bytes: measurement.bytes }
} catch (error) {
// Chrome may expose the API while disabling it in a particular headless mode.
}
}
if (performance.memory && Number.isFinite(performance.memory.usedJSHeapSize)) return { api: 'performance.memory.usedJSHeapSize', bytes: performance.memory.usedJSHeapSize }
return { api: 'unavailable', bytes: null }
}
const waitFor = (worker, type, timeoutMs = 180000) => new Promise((resolve, reject) => {
const timer = setTimeout(() => reject(new Error(`${type} timeout; last=${JSON.stringify(lastWorkerEvent)}`)), timeoutMs)
const listener = (event) => {
lastWorkerEvent = event.data
if (event.data.type === 'error') { clearTimeout(timer); worker.removeEventListener('message', listener); reject(new Error(event.data.error)); return }
if (event.data.type !== type) return
clearTimeout(timer); worker.removeEventListener('message', listener); resolve(event.data)
}
const errorListener = (event) => { clearTimeout(timer); worker.removeEventListener('message', listener); worker.removeEventListener('error', errorListener); reject(new Error(event.message || `${type} worker error`)) }
worker.addEventListener('message', listener)
worker.addEventListener('error', errorListener)
})
const run = async () => {
const browserId = new URL(location.href).searchParams.get('browser') || 'unknown'
const result = { schemaVersion: 1, browserId, crossOriginIsolated: globalThis.crossOriginIsolated === true, browser: navigator.userAgent, opfs: 'pending', initialize: 'pending', history: 'pending', memory: 'pending', abort: 'pending', crashRecovery: 'pending' }
const verifyOpfs = async () => {
const opfsRoot = await navigator.storage.getDirectory()
const opfsDirectory = await opfsRoot.getDirectoryHandle('bitbybit-browser-gate', { create: true })
const opfsFile = await opfsDirectory.getFileHandle(`${browserId}.probe`, { create: true })
const opfsWritable = await opfsFile.createWritable()
await opfsWritable.write(`OPFS-${browserId}`)
await opfsWritable.close()
const opfsText = await (await opfsFile.getFile()).text()
if (opfsText !== `OPFS-${browserId}`) throw new Error('OPFS read-after-write verification failed.')
await opfsDirectory.removeEntry(`${browserId}.probe`)
return { status: 'pass', persistence: 'opfs', bytes: opfsText.length }
}
try {
result.opfs = await Promise.race([verifyOpfs(), new Promise((_, reject) => setTimeout(() => reject(new Error('OPFS API did not respond within the browser capability window.')), 5000))])
} catch (error) {
throw error
}
let worker = new Worker('/native/occt-history/browser-harness-worker.js', { type: 'module' })
worker.postMessage({ type: 'initialize' })
const ready = await waitFor(worker, 'ready')
result.initialize = ready.occtVersion
worker.postMessage({ type: 'history', requestId: 'history-1', operation: 'cut' })
const history = await waitFor(worker, 'history')
result.history = { provider: history.provider, recordCount: history.recordCount }
const stressIterations = 1000
const browserBefore = await measureBrowserMemory()
worker.postMessage({ type: 'stress', requestId: 'stress-1', iterations: stressIterations })
const stress = await waitFor(worker, 'stress')
const browserAfter = await measureBrowserMemory()
const browserRetainedBytes = browserBefore.bytes === null || browserAfter.bytes === null ? null : browserAfter.bytes - browserBefore.bytes
result.memory = {
iterations: stress.iterations,
budgets: memoryBudgets,
wasm: stress.wasm,
browser: { before: browserBefore, after: browserAfter, retainedBytes: browserRetainedBytes },
gpu: stress.gpu,
ownership: stress.ownership
}
result.memory.withinBudget =
stress.wasm.peakHeapBytes <= memoryBudgets.wasmPeakBytes &&
stress.wasm.retainedBytes <= memoryBudgets.wasmRetainedBytes &&
browserRetainedBytes !== null && browserRetainedBytes <= memoryBudgets.browserRetainedBytes &&
stress.gpu.live <= memoryBudgets.gpuLiveResources &&
stress.ownership.live === 0 && stress.ownership.created === stress.ownership.released
worker.postMessage({ type: 'cancel', requestId: 'cancel-1' })
const cancelled = await waitFor(worker, 'cancelled')
result.abort = cancelled.requestId === 'cancel-1' ? 'recovered' : 'failed'
worker.terminate()
worker = new Worker('/native/occt-history/browser-harness-worker.js', { type: 'module' })
worker.postMessage({ type: 'initialize' })
const restarted = await waitFor(worker, 'ready')
result.crashRecovery = restarted.occtVersion === ready.occtVersion ? 'recovered' : 'failed'
worker.terminate()
if (!result.crossOriginIsolated || result.opfs?.status !== 'pass' || result.opfs.persistence !== 'opfs' || result.abort !== 'recovered' || result.crashRecovery !== 'recovered' || result.memory.iterations !== stressIterations || result.memory.withinBudget !== true) throw new Error('Browser isolation, OPFS, Worker recovery or memory budget contract failed.')
return result
}
try {
const result = await run()
output.textContent = `BROWSER_OCCT_RESULT=${JSON.stringify(result)}`
await fetch('/__occt-report', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(result) })
} catch (error) {
const result = { status: 'failed', error: error instanceof Error ? error.message : String(error) }
output.textContent = `BROWSER_OCCT_RESULT=${JSON.stringify(result)}`
await fetch('/__occt-report', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(result) })
throw error
}
</script>