I have been maintaining a utility userscript for a social media site for a few months now, and after a lot of promising user feedback I'm looking to make the jump to recreating it as a fully-fledged web extension.
However, one of the key features of the script and future extension involves running a hook function at document-start
that defines custom getters and setters for a specific window property used by the website at runtime. My current script makes use of a rather convoluted method:
const main = () => {
// hook function
};
const getNonce = () => {
const { nonce } = [...document.scripts].find(script => script.nonce) || '';
if (nonce === '') console.error('empty script nonce attribute: script may not inject');
return nonce;
};
const script = () => {
const s = document.createElement("script");
s.innerHTML = `const main = ${main.toString()}; main();`;
s.nonce = getNonce();
return s;
}
if (!document.head) {
const newNodes = [];
const findHead = () => {
const nodes = newNodes.splice(0);
if (nodes.length !== 0 && (nodes.some(node => node.matches('head') || node.querySelector('head') !== null))) {
const head = nodes.find(node => node.matches('head'));
head.append(script());
}
};
const observer = new MutationObserver(mutations => {
const nodes = mutations
.flatMap(({ addedNodes }) => [...addedNodes])
.filter(node => node instanceof Element)
.filter(node => node.isConnected);
newNodes.push(...nodes);
findHead();
});
observer.observe(document.documentElement, { childList: true, subtree: true });
} else document.head.append(script);
While this works fine as for a userscript, and functions exactly the same when migrated to a web extension content script, it's clear that there is certainly a better method with which to accomplish the same goal in a less...hacky manner. I've done a lot of digging through the MDN webExtension docs, but nothing I've come across so far has enlightened me as to what exactly this superior option may be.