Files
2026-10-13 10:34:48 +02:00

37 lines
3.2 KiB
JavaScript

'use strict';
function developerShortcut(input){return input?.type==='keyDown'&&(input.key==='F12'||((input.control||input.meta)&&input.shift&&!input.alt&&['i','j'].includes(String(input.key).toLowerCase())));}
function developerAssetRequestAllowed(details,{ready,policy,tabs}={}){
if(!ready||policy?.browser?.allowDevTools!==true||Date.parse(policy.expiresAt)<=Date.now()||!Number.isSafeInteger(details?.webContentsId))return false;
let url;try{url=new URL(details.url);}catch{return false;}
if(url.protocol!=='devtools:'||url.hostname!=='devtools'||!url.pathname.startsWith('/bundled/')||url.username||url.password)return false;
return tabs?.some(tab=>{if(tab.view.webContents.isDestroyed())return false;const frontend=tab.security?.developerFrontend||tab.view.webContents.devToolsWebContents;return frontend&&!frontend.isDestroyed()&&frontend.id===details.webContentsId;})===true;
}
function createDeveloperToolsGuard({contents,getContext,isCurrent,audit,createFrontend}={}){
if(!contents||[getContext,isCurrent,audit].some(fn=>typeof fn!=='function'))throw TypeError('Native developer-tools callbacks required');
let opening=false,openedState=false,frontend;
const disposeFrontend=()=>{const previous=frontend;frontend=null;if(previous&&!previous.isDestroyed())previous.destroy();};
const close=()=>{openedState=false;if(!contents.isDestroyed())contents.closeDevTools();disposeFrontend();};
const allowed=context=>!!context?.ready&&context.foreground===true&&context.policy?.browser?.allowDevTools===true&&Date.parse(context.policy.expiresAt)>Date.now()&&/^https?:\/\//.test(context.url||'')&&!contents.isDestroyed();
async function toggle(){
if(contents.isDestroyed())return;
if(openedState||contents.isDevToolsOpened()){close();await audit('devtools.closed');return;}
const context=getContext();
if(!allowed(context)||opening){await audit('devtools.denied');return;}
opening=true;
try{
// Publication and activation changes across audit persistence invalidate this grant.
await audit('devtools.open-requested');
if(!allowed(getContext())||!isCurrent(context)){await audit('devtools.denied');return;}
if(createFrontend){frontend=createFrontend();contents.setDevToolsWebContents(frontend.webContents);frontend.once('closed',()=>{if(!contents.isDestroyed()&&(openedState||contents.isDevToolsOpened()))contents.closeDevTools();});}
contents.openDevTools({mode:'detach',activate:true,title:'S Browser · Privileged developer tools'});
}finally{opening=false;}
}
const opened=()=>{openedState=true;const context=getContext();if(!allowed(context))close();else {frontend?.show();Promise.resolve(audit('devtools.opened')).catch(close);}};
const closed=()=>{openedState=false;disposeFrontend();};
contents.on('devtools-opened',opened);
contents.on('devtools-closed',closed);
contents.once('destroyed',disposeFrontend);
return {handleInput:(event,input)=>{if(!developerShortcut(input))return false;event.preventDefault();toggle().catch(close);return true;},toggle,close,isOpened:()=>openedState,dispose:()=>{contents.removeListener('devtools-opened',opened);contents.removeListener('devtools-closed',closed);close();}};
}
module.exports={createDeveloperToolsGuard,developerShortcut,developerAssetRequestAllowed};