My JavaScript book is out! Don't miss the opportunity to upgrade your beginner or average dev skills.

Wednesday, August 24, 2011

Simulate Script Injection Via Data URI

Well, not only downloads on the fly, the data uri works for almost everything ( only iOS 5 beta does not want to work with inline data uri AUDIO sources .... but this is another story ... ) ... so ...

How To Simulate Script injection

Let's say you want a test but you don't want to bother a server. However, you want to be sure the test is asynchronous and it simulates the server.

var
head = document.getElementsByTagName("head")[0],
script = document.createElement("script")
;
head.insertBefore(script, head.lastChild);
script.src = "data:text/javascript;base64," + btoa(
"alert('Hello World')"
);


How To Simulate JSONP

Same trick, isn't it? ... except:

script.src = "data:text/javascript;base64," + btoa(
"callback(" + JSON.stringify(dummyData) + ")"
);


How To Drop Server Requests

well, this is the tricky one ...
document.createElement = function (
createElement, // the native one
createResponse // the function "in charge"
) {
return function (nodeName) {
var result, src;
// if we are creating a script
if (/^script$/i.test(nodeName)) {
// result will be a place holder
result = createElement.call(
document,
"meta"
);
// we need to monitor the src property
Object.defineProperty(result, "src", {
get: function () {
return src;
},
// when set ...
set: function ($src) {
// we can check periodically ...
function T() {
// if the placeholder is in DOM
if (result.parentNode) {
// in this case we can put a real script
result = result.parentNode.insertBefore(
createElement.call(
document,
"script"
),
result
);
// and set the encoded src
result.src = "data:text/javascript;base64," +
btoa(createResponse.call(result, src))
;
} else {
// no DOM, no loading ... try later
setTimeout(T, 100);
}
}
// store the src
src = $src;
// and start checking
T();
}
});
} else {
// just return the element
result = createElement.call(
document,
nodeName
);
}
return result;
};
}(
document.createElement,
// must return a string
function (src) {
// this points to the current script
// src is the address
// if we know the callback ...
if (/callback=([^&]+)/.test(src)) {
return RegExp.$1 + '(' +
JSON.stringify({dummy:"data"})
+ ')';
}
}
);
// example
function test(data) {
alert(data.dummy);
}
document.documentElement.insertBefore(
document.createElement("script"),
document.documentElement.lastChild
).src = "page.php?callback=test";
view raw fake-script.js hosted with ❤ by GitHub

Surely there is some job to do in the createResponse() function but ... hey, we can stop bothering servers now ;)

3 comments:

check_ca said...

What about not encoding into base64 the script src ?

e.g.:

script.src = "data:text/javascript;,alert('Hello World')";

Andrea Giammarchi said...

it may have unexpected behavior with non ascii char due attribute restrictions.

Indeed even btoa is not enough, base64.encode(whateverItIs) is much better

Andrea Giammarchi said...

P.S. btw ... that was kinda *not* the point about the whole post but thanks for pointing it out