mirror of
https://github.com/tcsenpai/Zundler.git
synced 2025-06-06 11:35:40 +00:00
Improve performance by using messages
Instead of inserting the entire global context into the iframe's HTML, where we need to encode it to and decode it from JSON and base64, we only insert a stripped global context, i.e. without the file tree and the utils. If these are needed in the iframe, for example when a script changes the DOM and needs to load an image, we use messages to communicate with the parent document and retrieve the file in question.
This commit is contained in:
parent
c2373c6493
commit
b40354235a
@ -66,37 +66,21 @@ window.history.replaceState = myReplaceState;
|
||||
|
||||
const { fetch: originalFetch } = window;
|
||||
|
||||
async function waitFor(predicate, timeout) {
|
||||
function waitForParentResponse(path) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const check = () => {
|
||||
if (!predicate()) return;
|
||||
clearInterval(interval);
|
||||
resolve();
|
||||
};
|
||||
const interval = setInterval(check, 100);
|
||||
check();
|
||||
|
||||
if (!timeout) return;
|
||||
setTimeout(() => {
|
||||
clearInterval(interval);
|
||||
reject();
|
||||
}, timeout);
|
||||
retrieveFileFromParent(path, file => {
|
||||
resolve(file);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
window.fetch = async (...args) => {
|
||||
// wait until globalContext is ready
|
||||
try {
|
||||
await waitFor(() => window.hasOwnProperty("globalContext"), 10000);
|
||||
} catch (err) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
window.fetch = async (...args) => {
|
||||
let [resource, config ] = args;
|
||||
var path = normalizePath(resource);
|
||||
var response;
|
||||
if (isVirtual(path)) {
|
||||
var file = retrieveFile(path);
|
||||
var file = await waitForParentResponse(path);
|
||||
var data = file.data;
|
||||
if (file.base64encoded) {
|
||||
data = _base64ToArrayBuffer(data);
|
||||
@ -110,6 +94,47 @@ window.fetch = async (...args) => {
|
||||
};
|
||||
|
||||
|
||||
var retrieveFileFromParent = function(path, callback) {
|
||||
// Get the file into the iframe by messaging the parent document
|
||||
// console.log("Retrieving file from parent: " + path);
|
||||
|
||||
function messageHandler(event) {
|
||||
if (event.data.action === "sendFile" && event.data.argument.path === path) {
|
||||
callback(event.data.argument.file);
|
||||
window.removeEventListener('message', messageHandler);
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener('message', messageHandler);
|
||||
|
||||
window.parent.postMessage({
|
||||
action: "retrieveFile",
|
||||
argument: {
|
||||
path: path,
|
||||
}
|
||||
}, '*');
|
||||
};
|
||||
|
||||
|
||||
var embedImgFromParent = function(img) {
|
||||
function setSrc(img, file) {
|
||||
if (mime_type == 'image/svg+xml') {
|
||||
img.setAttribute('src', "data:image/svg+xml;charset=utf-8;base64, " + btoa(file.data));
|
||||
} else {
|
||||
img.setAttribute('src', `data:${file.mime_type};base64, ${file.data}`);
|
||||
}
|
||||
};
|
||||
|
||||
if (img.hasAttribute('src')) {
|
||||
const src = img.getAttribute('src');
|
||||
if (isVirtual(src)) {
|
||||
var path = normalizePath(src);
|
||||
retrieveFileFromParent(path, file => setSrc(img, file));
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
const observer = new MutationObserver((mutationList) => {
|
||||
// console.log("Fix mutated elements...", mutationList);
|
||||
mutationList.forEach((mutation) => {
|
||||
@ -118,7 +143,7 @@ const observer = new MutationObserver((mutationList) => {
|
||||
fixLink(a);
|
||||
});
|
||||
Array.from(mutation.target.querySelectorAll("img")).forEach( img => {
|
||||
embedImg(img);
|
||||
embedImgFromParent(img);
|
||||
});
|
||||
Array.from(mutation.target.querySelectorAll("form")).forEach( form => {
|
||||
fixForm(form);
|
||||
|
@ -2,19 +2,6 @@
|
||||
* Functions that will be needed by several files
|
||||
*/
|
||||
|
||||
var retrieveFile = function(path) {
|
||||
// console.log("Retrieving file: " + path);
|
||||
var fileTree = window.globalContext.fileTree;
|
||||
var file = fileTree[path];
|
||||
if (!file) {
|
||||
console.warn("File not found: " + path);
|
||||
return "";
|
||||
} else {
|
||||
return file;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
var isVirtual = function(url) {
|
||||
// Return true if the url should be retrieved from the virtual file tree
|
||||
var _url = url.toString().toLowerCase();
|
||||
@ -71,23 +58,6 @@ var fixForm = function(form) {
|
||||
};
|
||||
|
||||
|
||||
var embedImg = function(img) {
|
||||
if (img.hasAttribute('src')) {
|
||||
const src = img.getAttribute('src');
|
||||
if (isVirtual(src)) {
|
||||
var path = normalizePath(src);
|
||||
const file = retrieveFile(path);
|
||||
const mime_type = file.mime_type;
|
||||
if (mime_type == 'image/svg+xml') {
|
||||
img.setAttribute('src', "data:image/svg+xml;charset=utf-8;base64, " + btoa(file.data));
|
||||
} else {
|
||||
img.setAttribute('src', `data:${mime_type};base64, ${file.data}`);
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
var normalizePath = function(path) {
|
||||
// make relative paths absolute
|
||||
var result = window.globalContext.current_path;
|
||||
|
@ -1,5 +1,18 @@
|
||||
const iFrameId = 'zundler-iframe';
|
||||
|
||||
var retrieveFile = function(path) {
|
||||
// console.log("Retrieving file: " + path);
|
||||
var fileTree = window.globalContext.fileTree;
|
||||
var file = fileTree[path];
|
||||
if (!file) {
|
||||
console.warn("File not found: " + path);
|
||||
return "";
|
||||
} else {
|
||||
return file;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
var setFavicon = function(href) {
|
||||
if (!href) {return;}
|
||||
var favicon = document.createElement("link");
|
||||
@ -32,6 +45,25 @@ var createIframe = function() {
|
||||
}
|
||||
|
||||
|
||||
function deepCopyExcept(obj, skipProps) {
|
||||
if (obj === null || typeof obj !== 'object') {
|
||||
return obj;
|
||||
}
|
||||
|
||||
let result = Array.isArray(obj) ? [] : {};
|
||||
|
||||
for (let key in obj) {
|
||||
if (obj.hasOwnProperty(key)) {
|
||||
if (!skipProps.includes(key)) {
|
||||
result[key] = deepCopyExcept(obj[key], skipProps);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
var prepare = function(html) {
|
||||
function unicodeToBase64(string) {
|
||||
const utf8EncodedString = unescape(encodeURIComponent(string));
|
||||
@ -41,12 +73,18 @@ var prepare = function(html) {
|
||||
var parser = new DOMParser();
|
||||
var doc = parser.parseFromString(html, "text/html");
|
||||
|
||||
const gcTag = doc.createElement("script");
|
||||
// Insert the global context into the iframe's DOM, but without the file
|
||||
// tree or utils. They are not necessary; the iframe will message the
|
||||
// parent document to retrieve files.
|
||||
//
|
||||
// Convert JSON object to b64 because it contain all kinds of
|
||||
// problematic characters: `, ", ', &, </script>, ...
|
||||
// atob is insufficient, because it only deals with ASCII - we have
|
||||
// unicode
|
||||
var serializedGC = unicodeToBase64(JSON.stringify(window.globalContext));
|
||||
const gcTag = doc.createElement("script");
|
||||
const strippedGC = deepCopyExcept(window.globalContext, ["fileTree", "utils"]);
|
||||
|
||||
var serializedGC = unicodeToBase64(JSON.stringify(strippedGC));
|
||||
|
||||
gcTag.textContent = `
|
||||
function base64ToUnicode(base64String) {
|
||||
@ -82,6 +120,23 @@ var prepare = function(html) {
|
||||
}
|
||||
|
||||
|
||||
var embedImg = function(img) {
|
||||
if (img.hasAttribute('src')) {
|
||||
const src = img.getAttribute('src');
|
||||
if (isVirtual(src)) {
|
||||
var path = normalizePath(src);
|
||||
const file = retrieveFile(path);
|
||||
const mime_type = file.mime_type;
|
||||
if (mime_type == 'image/svg+xml') {
|
||||
img.setAttribute('src', "data:image/svg+xml;charset=utf-8;base64, " + btoa(file.data));
|
||||
} else {
|
||||
img.setAttribute('src', `data:${mime_type};base64, ${file.data}`);
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
var embedJs = function(doc) {
|
||||
Array.from(doc.querySelectorAll("script")).forEach( oldScript => {
|
||||
const newScript = doc.createElement("script");
|
||||
@ -181,8 +236,20 @@ window.onload = function() {
|
||||
if (evnt.data.action == 'ready') {
|
||||
hideLoadingIndicator();
|
||||
|
||||
} else if (evnt.data.action == 'retrieveFile') {
|
||||
const path = evnt.data.argument.path;
|
||||
const file = retrieveFile(path);
|
||||
iframe.contentWindow.postMessage({
|
||||
action: "sendFile",
|
||||
argument: {
|
||||
path: path,
|
||||
file: file,
|
||||
},
|
||||
}, "*");
|
||||
|
||||
} else if (evnt.data.action == 'showMenu') {
|
||||
showMenu();
|
||||
|
||||
} else if (evnt.data.action == 'set_title') {
|
||||
// iframe has finished loading and sent us its title
|
||||
// parent sets the title and responds with the globalContext object
|
||||
|
Loading…
x
Reference in New Issue
Block a user