Thursday, 11 July 2013

How to dynamically inject debuggable javascript into Chrome Dev Tools

I love Chrome Dev tools, I love how you can insert breakpoints and step through your source code. However, during my escapades in re-writing js source code on the fly for the realtime coding feature of the 3d multiplayer game maker I'm writing (Multi). I found a few interesting pain points.

For the following examples, please imagine that the code in the variable scriptText has been loaded from a cache.


Using Eval
If you use eval, the injected code will not be shown in Chrome Dev Tools.

jstest.html
<html><head></head><body></body>

<script>
var scriptText = "console.log( 'hello' );";
eval( scriptText );
</script>

</html>


Using script.text
If you create a script element then include the javascript code in the text tag for parsing, again, the script will not be shown in Chrome Dev Tools.


jstest.html
<html><head></head><body></body>

<script type="text/javascript" src="jstest.js"></script>

</html>


jstest.js
var head = document.getElementsByTagName( 'head' )[0];
var script = document.createElement( 'script' );
script.type = 'text/javascript';
head.appendChild( script );

var scriptText = "console.log( 'hello' );";
script.text = scriptText;

There is an open bug from 2011 on this issue which I've left a comment towards. So hopefully when this gets fixed, you'll no longer be able to do this trick.


Using Blobs
If you would like to see your code in the sources tab, then you should use the .src field to point to a blob containing your script.


jstest.js
var head = document.getElementsByTagName( 'head' )[0];
var script = document.createElement( 'script' );
script.type = 'text/javascript';
head.appendChild( script );

var scriptText = "console.log( 'hello' );";
script.src = window.URL.createObjectURL( new Blob( [scriptText] ) );



Notes
If you would like to get up to nefarious activities, eval and script.text is a great approach. However, if you're trying to generate some dynamic coding environments, then you're going to run into some problems. While using blobs is great to see the injected script, there are a few issues I'm still working through with them. It seems that if you inject a script to override a class you've previously injected, the interpreter will ignore your new script. So what I've ended up doing in the meantime, is using .src for a server side generated url, which works, but requires server-side setup.

If you have any solutions around this problem, I'd love to hear.