executor-window.js (2638B)
1 // Functions available by default in the executor. 2 3 'use strict'; 4 5 let executorStartEvent = null; 6 7 function requestExecutor(uuid, startOn) { 8 if (startOn) { 9 addEventListener(startOn, (e) => { 10 executorStartEvent = e; 11 startExecutor(uuid); 12 }); 13 } else { 14 startExecutor(uuid); 15 } 16 } 17 18 function addScript(url) { 19 const script = document.createElement('script'); 20 script.src = url; 21 const promise = new Promise((resolve, reject) => { 22 script.onload = () => resolve(url); 23 script.onerror = (e) => reject(e); 24 }); 25 document.body.appendChild(script); 26 return promise; 27 } 28 29 /** 30 * Suspends the executor and executes the function in `fnString` when it has 31 * suspended. Installs a pageshow handler to resume the executor if the 32 * document is BFCached. Installs a hashchange handler to detect when the 33 * navigation did not change documents. 34 * 35 * This returns nothing because fn is invoke after waiting for the document to 36 * be suspended. If we were to return a promise, the executor could not suspend 37 * until that promise resolved but the promise cannot resolve until the executor 38 * is suspended. This could be avoided by adding support 39 * directly in the dispatcher for tasks suspend immediately after execution. 40 * 41 * @param {string} fnString A stringified function to be executed. 42 * @param {any[]} args The arguments to pass to the function. 43 */ 44 function executeScriptToNavigate(fnString, args) { 45 // Only one of these listeners should run. 46 const controller = new AbortController(); 47 window.addEventListener('pageshow', (event) => { 48 controller.abort(); 49 executor.resume(); 50 }, {signal: controller.signal, once: true}); 51 window.addEventListener('hashchange', (event) => { 52 controller.abort(); 53 const oldURLObject = new URL(event.oldURL); 54 const newURLObject = new URL(event.newURL); 55 oldURLObject.hash = ''; 56 newURLObject.hash = ''; 57 // If only the hash-fragment changed then the navigation was 58 // same-document and we should resume the executor. 59 if (oldURLObject.toString() == newURLObject.toString()) { 60 executor.resume(); 61 } 62 }, {signal: controller.signal, once: true}); 63 64 executor.suspend(() => { 65 eval(fnString).apply(null, args); 66 }); 67 } 68 69 // If a frameset element exists in the document, return the first one. Otherwise 70 // create a new one and return that. 71 function findOrCreateFrameset() { 72 const framesets = document.getElementsByTagName('frameset'); 73 if (framesets.length > 0) { 74 return framesets[0]; 75 } 76 const frameset = document.createElement('frameset'); 77 frameset.cols = '100%'; 78 document.body.append(frameset); 79 return frameset; 80 }