// Phase B verification of the render-window network block (finding #5): // - a local data: image asset still loads and draws (block must not break it) // - an http: image asset is blocked and skipped without hanging the render // // Uses the REAL worker window (createRenderWindow) by loading render.html in a // window built the same way, so it exercises the actual webRequest filter. // // Run: node_modules/.bin/electron scripts/verify-assets.cjs const { app, BrowserWindow } = require('electron') const path = require('node:path') const { pathToFileURL } = require('node:url') const RENDER_PARTITION = 'ruwio-render' // 40x40 green square as an inline SVG data URL (no network needed). const greenSvg = "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='40' height='40'%3E%3Crect width='40' height='40' fill='%2300ff00'/%3E%3C/svg%3E" function makeScene(src) { return { canvas: { width: 160, height: 120, background: '#202030' }, duration: 1, assets: [{ id: 'img1', kind: 'image', width: 40, height: 40, src }], layers: [ { id: 'l1', assetId: 'img1', visible: true, base: { x: 80, y: 60, scaleX: 1, scaleY: 1, rotation: 0, opacity: 1 }, keyframes: [], }, ], } } function createRenderWindow(scene) { const win = new BrowserWindow({ width: scene.canvas.width, height: scene.canvas.height, show: false, frame: false, transparent: false, backgroundColor: '#ffffff', webPreferences: { contextIsolation: true, nodeIntegration: false, sandbox: true, partition: RENDER_PARTITION, }, }) win.webContents.session.webRequest.onBeforeRequest({ urls: ['*://*/*'] }, (_d, cb) => cb({ cancel: true }), ) win.webContents.setWindowOpenHandler(() => ({ action: 'deny' })) win.webContents.on('will-navigate', (e) => e.preventDefault()) return win } function countGreen(image) { const bmp = image.getBitmap() // BGRA let green = 0 for (let i = 0; i < bmp.length; i += 4) { if (bmp[i] < 120 && bmp[i + 1] > 150 && bmp[i + 2] < 120) green++ } return green } async function renderOnce(src) { const scene = makeScene(src) const win = createRenderWindow(scene) const renderHtml = path.join(process.cwd(), 'server', 'render.html') await win.loadURL(pathToFileURL(renderHtml).toString()) // Bound by Promise.race so a network-blocked asset that mis-handles can't hang. const initDone = win.webContents.executeJavaScript( `window.__animatrInit(${JSON.stringify(scene)})`, true, ) const timedOut = await Promise.race([ initDone.then(() => false), new Promise((r) => setTimeout(() => r(true), 5000)), ]) await win.webContents.executeJavaScript('window.__animatrSeek(0)', true) const img = await win.webContents.capturePage({ x: 0, y: 0, width: 160, height: 120 }) const green = countGreen(img) if (!win.isDestroyed()) win.close() return { green, timedOut } } app.on('window-all-closed', () => {}) app.whenReady().then(async () => { const local = await renderOnce(greenSvg) const remote = await renderOnce('http://10.255.255.1/never.png') // unroutable; must not hang const notes = [] let pass = true // ~1600 green px expected for a 40x40 image; allow for AA. if (local.green < 1000) { pass = false; notes.push(`local data: image did not draw (green=${local.green})`) } if (local.timedOut) { pass = false; notes.push('local image init timed out') } if (remote.green > 50) { pass = false; notes.push(`remote image drew despite block (green=${remote.green})`) } if (remote.timedOut) { pass = false; notes.push('remote (blocked) image init hung past 5s') } console.log(`local: green=${local.green} timedOut=${local.timedOut}`) console.log(`remote(blocked): green=${remote.green} timedOut=${remote.timedOut}`) console.log(notes.length ? 'NOTES:\n ' + notes.join('\n ') : 'NOTES: none') console.log(pass ? 'RESULT: PASS' : 'RESULT: FAIL') app.exit(pass ? 0 : 1) })