Skip to content

Instantly share code, notes, and snippets.

@bluesmoon
Last active January 3, 2024 20:40
Show Gist options
  • Star 9 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save bluesmoon/675c37368ddd58dfa5f578f1e5a59778 to your computer and use it in GitHub Desktop.
Save bluesmoon/675c37368ddd58dfa5f578f1e5a59778 to your computer and use it in GitHub Desktop.
A CSP compliant non-blocking script loader
<script id="nb-loader-script">
(function(url) {
// document.currentScript works on most browsers, but not all
var where = document.currentScript || document.getElementById("nb-loader-script"),
promoted = false,
LOADER_TIMEOUT = 3000,
IDPREFIX = "__nb-script";
// function to promote a preload link node to an async script node
function promote() {
var s;
s = document.createElement("script");
s.id = IDPREFIX + "-async";
s.src = url;
where.parentNode.appendChild(s);
promoted = true;
}
// function to load script in an iframe on browsers that don't support preload hints
function iframe_loader() {
promoted = true;
var win, doc, dom, s, bootstrap, iframe = document.createElement("iframe");
// IE6, which does not support CSP, treats about:blank as insecure content, so we'd have to use javascript:void(0) there
// In browsers that do support CSP, javascript:void(0) is considered unsafe inline JavaScript, so we prefer about:blank
iframe.src = "about:blank";
// We set title and role appropriately to play nicely with screen readers and other assistive technologies
iframe.title = "";
iframe.role = "presentation";
s = (iframe.frameElement || iframe).style;
s.width = 0; s.height = 0; s.border = 0; s.display = "none";
where.parentNode.insertBefore(iframe, where);
try {
win = iframe.contentWindow;
doc = win.document.open();
}
catch (e) {
// document.domain has been changed and we're on an old version of IE, so we got an access denied.
// Note: the only browsers that have this problem also do not have CSP support.
// Get document.domain of the parent window
dom = document.domain;
// Set the src of the iframe to a JavaScript URL that will immediately set its document.domain to match the parent.
// This lets us access the iframe document long enough to inject our script.
// Our script may need to do more domain massaging later.
iframe.src = "javascript:var d=document.open();d.domain='" + dom + "';void(0);";
win = iframe.contentWindow;
doc = win.document.open();
}
bootstrap = function() {
// This code runs inside the iframe
var js = doc.createElement("script");
js.id = IDPREFIX + "-iframe-async";
js.src = url;
doc.body.appendChild(js);
};
try {
win._l = bootstrap
if (win.addEventListener) {
win.addEventListener("load", win._l, false);
}
else if (win.attachEvent) {
win.attachEvent("onload", win._l);
}
}
catch (f) {
// unsafe version for IE8 compatability
// If document.domain has changed, we can't use win, but we can use doc
doc._l = function() {
if (dom) {
this.domain = dom;
}
bootstrap();
}
doc.write('<body onload="document._l();">');
}
doc.close();
}
// We first check to see if the browser supports preload hints via a link element
var l = document.createElement("link");
if (l.relList && typeof l.relList.supports === "function" && l.relList.supports("preload") && ("as" in l)) {
l.href = url;
l.rel = "preload";
l.as = "script";
// If the link successfully preloads our script, we'll promote it to a script node.
l.addEventListener("load", promote);
// If the preload fails or times out, we'll fallback to the iframe loader
l.addEventListener("error", iframe_loader);
setTimeout(function() {
if (!promoted) {
iframe_loader();
}
}, LOADER_TIMEOUT);
where.parentNode.appendChild(l);
}
else {
// If preload hints aren't supported, then fallback to the iframe loader
iframe_loader();
}
})("https://your.script.url/goes/here.js");
</script>
(function _check_doc_domain(domain) {
// This snippet tries to walk document.domain in case the parent frame changed it after our iframe was created and the script was loaded
/*eslint no-unused-vars:0*/
var test;
if (!window) {
return;
}
// If domain is not passed in, then this is a global call
// domain is only passed in if we call ourselves, so we
// skip the frame check at that point
if (typeof domain === "undefined") {
// If we're running in the main window, then we don't need this
if (window.parent === window || !document.getElementById("__nb-script-iframe-async")) {
return;// true; // nothing to do
}
try {
// If document.domain is changed during page load (from www.blah.com to blah.com, for example),
// window.parent.window.location.href throws "Permission Denied" in IE.
// Resetting the inner domain to match the outer makes location accessible once again
if (window.document.domain !== window.parent.window.document.domain) {
window.document.domain = window.parent.window.document.domain;
}
}
catch (err) {
// We could log this, but nothing else to do
}
}
domain = document.domain;
if (domain.indexOf(".") === -1) {
// we've reached the top level domain
return;// false; // not okay, but we did our best
}
// 1. Test without setting document.domain
try {
test = window.parent.document;
return;// test !== undefined; // all okay
}
// 2. Test with document.domain
catch (err) {
document.domain = domain;
}
try {
test = window.parent.document;
return;// test !== undefined; // all okay
}
// 3. Strip off leading part and try again
catch (err) {
domain = domain.replace(/^[\w\-]+\./, "");
}
_check_doc_domain(domain);
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment