Add scoped native history and window agent controls
This commit is contained in:
1 parent
adf0940e5f
commit
61fcd8336f
27 files changed
+503
-25
No files matched your search
@@ -1,14 +1,17 @@
|
||||
'use strict';
|
||||
// Native history only: pages and agents cannot supply a navigation target.
|
||||
async function performHistory({contents,action,allowed,current,signal,timeoutMs=10000}={}) {
|
||||
if(!contents||!['back','forward','reload'].includes(action)||typeof allowed!=='function'||typeof current!=='function'||!Number.isFinite(timeoutMs)||timeoutMs<1||timeoutMs>60000)throw Error('Invalid history operation');
|
||||
async function performHistory({contents,action,allowed,current,signal,index,getSnapshot,historyBinding,timeoutMs=10000}={}) {
|
||||
if(!contents||!['back','forward','reload','index'].includes(action)||typeof allowed!=='function'||typeof current!=='function'||!Number.isFinite(timeoutMs)||timeoutMs<1||timeoutMs>60000||action==='index'&&(!Number.isSafeInteger(index)||index<0||typeof getSnapshot!=='function'||typeof historyBinding!=='string'))throw Error('Invalid history operation');
|
||||
const guard=async(phase='before')=>{if(signal?.aborted)throw Error('History operation cancelled');if(contents.isDestroyed?.())throw Error('History contents unavailable');if(await current(phase)!==true)throw Error('History context changed');if(signal?.aborted)throw Error('History operation cancelled');};
|
||||
const target=()=>action==='reload'?contents.getURL():contents.navigationHistory.getEntryAtIndex(contents.navigationHistory.getActiveIndex()+(action==='back'?-1:1))?.url;
|
||||
const target=()=>action==='reload'?contents.getURL():contents.navigationHistory.getEntryAtIndex(action==='index'?index:contents.navigationHistory.getActiveIndex()+(action==='back'?-1:1))?.url;
|
||||
await guard();const url=target();
|
||||
if(!url)return {completed:false,url:contents.getURL()};
|
||||
let parsed;try{parsed=new URL(url);}catch{throw Error('Invalid history destination');}
|
||||
if(!['http:','https:'].includes(parsed.protocol)||parsed.username||parsed.password||!await allowed(url))throw Error('History destination denied');
|
||||
await guard();if(target()!==url)throw Error('History destination changed');
|
||||
if(action==='index'&&index===contents.navigationHistory.getActiveIndex()){
|
||||
if(JSON.stringify(getSnapshot())!==historyBinding||contents.getURL()!==url)throw Error('History snapshot changed');return {completed:true,url};
|
||||
}
|
||||
return new Promise((resolve,reject)=>{
|
||||
let settled=false,dispatched=false,checking=false;
|
||||
const cleanup=()=>{clearTimeout(timer);signal?.removeEventListener('abort',abort);for(const [name,fn] of listeners)contents.removeListener(name,fn);};
|
||||
@@ -21,7 +24,7 @@ async function performHistory({contents,action,allowed,current,signal,timeoutMs=
|
||||
const timer=setTimeout(()=>fail(Error('History operation timed out')),timeoutMs);
|
||||
for(const [name,fn] of listeners)contents.on(name,fn);signal?.addEventListener('abort',abort,{once:true});
|
||||
if(signal?.aborted){abort();return;}
|
||||
try{dispatched=true;if(action==='reload')contents.reload();else contents.navigationHistory.goToIndex(contents.navigationHistory.getActiveIndex()+(action==='back'?-1:1));}catch(error){fail(error);}
|
||||
try{if(action==='index'&&JSON.stringify(getSnapshot())!==historyBinding)throw Error('History snapshot changed');if(target()!==url)throw Error('History destination changed');dispatched=true;if(action==='reload')contents.reload();else contents.navigationHistory.goToIndex(action==='index'?index:contents.navigationHistory.getActiveIndex()+(action==='back'?-1:1));}catch(error){fail(error);}
|
||||
});
|
||||
}
|
||||
module.exports={performHistory};
|
||||
Reference in new issue
Block a user