diff --git a/web/zebra-spaces.html b/web/zebra-spaces.html index 6975f49..c47fa1d 100644 --- a/web/zebra-spaces.html +++ b/web/zebra-spaces.html @@ -4618,6 +4618,30 @@ async function sfuPublishCamera(){ 'camera open cancelled: '+(e.name||'Error')+' — '+e.message+ ' (constraints: 1280x720@30 16:9'+(exact?' deviceId='+String(exact).slice(0,8):'')+')' ); + /* Diagnostic snapshot — runs AFTER getUserMedia returned, so the + * await doesn't break transient activation for any subsequent + * retry. Captures (1) Permissions API state for camera (what + * Firefox itself says about the permission, separate from + * getUserMedia's return), (2) document visibility (Firefox auto- + * denies for hidden documents), (3) user-activation state (gesture + * propagation). Lets us tell apart "saved deny" / "global pref + * block" / "no gesture" / "hidden doc" without another roundtrip. + * Fox ticket 0002. */ + let permState = 'n/a', visState = 'n/a', activation = 'n/a'; + try { + if (navigator.permissions && navigator.permissions.query){ + const p = await navigator.permissions.query({ name: 'camera' }); + permState = p.state; + } + } catch(_){} + try { visState = document.visibilityState || 'n/a'; } catch(_){} + try { + if (navigator.userActivation){ + activation = (navigator.userActivation.isActive ? 'active' : 'inactive') + + '/hasBeenActive=' + (navigator.userActivation.hasBeenActive ? 1 : 0); + } + } catch(_){} + logLine('', 'camera permission probe: state='+permState+' vis='+visState+' activation='+activation); /* OverconstrainedError + a saved deviceId is the canonical "saved * device gone or renamed" case — retry once without deviceId so the * user gets default camera rather than a permanent fail. */ @@ -8161,8 +8185,8 @@ logLine('', 'ready — pick a handle, type a rendezvous code, enter the space');