Release Vulpes OS 2.7-preview.24

This commit is contained in:
OverSu 2026-11-04 12:52:20 +01:00
commit 97ef99f827
135 changed files with 7717 additions and 2524 deletions

View file

@ -1,4 +1,5 @@
import {tundraRadio} from 'resource://vulpes/host/Tundra.sys.mjs';
import {Runtime} from 'resource://vulpes/host/Runtime.sys.mjs';
import {setTimeout} from 'resource://gre/modules/Timer.sys.mjs';
const sleep=ms=>new Promise(resolve=>setTimeout(resolve,ms));
@ -164,6 +165,62 @@ async function run(win) {
await key('Escape');
}
}
// Exercise the real Tundra worker with the exact 320x240 preview mode
// used by KaiOS on beatles. Keep the sample in memory and release Camera1
// immediately; this is a smoke check, not a retained photo or recording.
const cameraActor={};
let cameraSession;
try {
const metadata=await Runtime.platform.nativeCamera.request(cameraActor,{action:'start',camera:'back'});
cameraSession=metadata.session;
const frame=await Runtime.platform.nativeCamera.request(cameraActor,{action:'frame',session:cameraSession});
const data=new Uint8Array(frame.data);
const pixels=metadata.width*metadata.height;
let sum=0,min=255,max=0;
for(let i=0;i<Math.min(pixels,data.length);i++) { const value=data[i];sum+=value;min=Math.min(min,value);max=Math.max(max,value); }
report.cameraPreview={width:metadata.width,height:metadata.height,sequence:frame.sequence,
bytes:data.length,yMean:data.length?Number((sum/Math.min(pixels,data.length)).toFixed(2)):null,yMin:data.length?min:null,yMax:data.length?max:null,
passed:data.length===pixels*3/2 && max>32};
const picture=await Runtime.platform.nativeCamera.request(cameraActor,{action:'picture',session:cameraSession,size:{width:1600,height:1200}});
const jpeg=new Uint8Array(picture.data);
report.cameraPicture={bytes:jpeg.length,jpeg:jpeg[0]===0xff&&jpeg[1]===0xd8,passed:jpeg.length>4096&&jpeg[0]===0xff&&jpeg[1]===0xd8};
} catch(error) {
report.cameraPreview={passed:false,error:error.message || String(error)};
} finally {
if(cameraSession!==undefined) {
try { await Runtime.platform.nativeCamera.request(cameraActor,{action:'stop',session:cameraSession}); }
catch(error) { report.cameraPreview={...report.cameraPreview,stopError:error.message || String(error)}; }
}
}
report.cameraApps=Runtime.apps.filter(app=>/camera|photo/i.test(app.id+' '+app.name+' '+app.origin)).map(app=>({id:app.id,name:app.name,origin:app.origin,manifestURL:app.manifestURL}));
try {
// Match the URL-based launcher used by the other Gaia app audits. The
// app registry's internal ID is not stable across Gaia manifests.
await evaluate('system',`return await VulpesCompat.call('apps.launch',{manifestURL:'http://camera.localhost:8765/manifest.webapp'});`);
for(let i=0;i<120&&!find(system,'camera');i++)await sleep(250);
if(find(system,'camera')) {
for(let i=0;i<120;i++) {
const state=await evaluate('camera',"return {ready:document.readyState==='complete',app:typeof app!=='undefined'&&!!app.camera,busy:typeof app!=='undefined'&&!!app.camera?.isBusy};");
if(state.ready&&state.app&&(!state.busy||i>=119))break;
await sleep(250);
}
await sleep(2500);
report.cameraApp=await evaluate('camera',"var v=document.querySelector('.js-video'),c=document.querySelector('canvas[data-vulpes-native-preview]'),a=typeof app==='undefined'?null:app,cam=a?.camera,stream=cam?.mozCamera,canvasLuma=null;try{if(c){const t=document.createElement('canvas');t.width=80;t.height=60;const x=t.getContext('2d',{willReadFrequently:true});x.drawImage(c,0,0,80,60);const p=x.getImageData(0,0,80,60).data;let sum=0,min=255,max=0;for(let i=0;i<p.length;i+=4){const y=(77*p[i]+150*p[i+1]+29*p[i+2])>>8;sum+=y;min=Math.min(min,y);max=Math.max(max,y);}canvasLuma={mean:Number((sum/4800).toFixed(1)),min,max};}}catch(e){canvasLuma={error:e.message||String(e)};}return {loaded:document.readyState==='complete',readyState:document.readyState,url:location.href,title:document.title,viewfinder:!!v,previewCanvas:!!c,canvasSize:c?[c.width,c.height]:null,canvasLuma,videoHidden:v?getComputedStyle(v).display==='none':false,documentHidden:document.hidden,visibility:document.visibilityState,errorText:document.querySelector('.overlay[data-type=\"request-fail\"] #overlay-text')?.textContent,bodyText:document.body?.innerText.slice(0,240),appGlobal:!!a,cameraObject:!!cam,cameraBusy:cam?.isBusy,cameraReady:!!stream&&!cam.isBusy,mozCamera:!!stream,previewFrames:stream?.vulpesTiming?.frames||0,previewClosed:stream?.vulpesClosed,mode:cam?.mode||null,cameraList:cam?.cameraList};");
if(report.cameraApp.cameraReady) {
try {
await evaluate('camera',"await app.camera.mozCamera.vulpesCameraControl({action:'flash',mode:'torch'});return true;");
await sleep(1500);
report.cameraApp.torchCanvas=await evaluate('camera',"const c=document.querySelector('canvas[data-vulpes-native-preview]'),t=document.createElement('canvas');t.width=80;t.height=60;const x=t.getContext('2d',{willReadFrequently:true});x.drawImage(c,0,0,80,60);const p=x.getImageData(0,0,80,60).data;let sum=0,min=255,max=0;for(let i=0;i<p.length;i+=4){const y=(77*p[i]+150*p[i+1]+29*p[i+2])>>8;sum+=y;min=Math.min(min,y);max=Math.max(max,y);}return {mean:Number((sum/4800).toFixed(1)),min,max};");
} catch(error) { report.cameraApp.torchCanvas={error:error.message||String(error)}; }
finally {
try { await evaluate('camera',"await app.camera.mozCamera.vulpesCameraControl({action:'flash',mode:'off'});return true;"); }
catch(error) { report.cameraApp.torchOffError=error.message||String(error); }
}
try { report.cameraApp.picture=await evaluate('camera',"const b=await app.camera.mozCamera.takePicture(),d=new Uint8Array(await b.arrayBuffer());let bitmap=null;try{bitmap=await createImageBitmap(b);const c=document.createElement('canvas');c.width=80;c.height=60;const x=c.getContext('2d',{willReadFrequently:true});x.drawImage(bitmap,0,0,c.width,c.height);const p=x.getImageData(0,0,c.width,c.height).data;let sum=0,min=255,max=0;for(let i=0;i<p.length;i+=4){const y=(77*p[i]+150*p[i+1]+29*p[i+2])>>8;sum+=y;min=Math.min(min,y);max=Math.max(max,y);}return {bytes:b.size,type:b.type,jpeg:d[0]===255&&d[1]===216,width:bitmap.width,height:bitmap.height,lumaMean:Number((sum/(p.length/4)).toFixed(1)),lumaMin:min,lumaMax:max};}finally{bitmap?.close();}"); }
catch(error) { report.cameraApp.picture={error:error.message||String(error)}; }
}
} else report.cameraApp={loaded:false,launch:'sent',apps:report.cameraApps};
} catch(error) { report.cameraApp={loaded:false,error:error.message||String(error),apps:report.cameraApps}; }
}catch(error){report.error=String(error);report.stack=error.stack;}
await save();
}