Wednesday, April 19, 2006

Remote Scripting on Google's BlogSpot

Create eye-candy visual effect in web applications requires extensive use of Javascript. There are tons of javascript framework works out there to use. However, I faced one of the most challenging yet fundamental issue in javascript: cross-domain scripting (CDS).

CDS restriction implemented in standard web browsers to prevent the execution of scripts reside in a different domain from the current domain hosting the web applications. CDS restriction's primary purpose is against malicious scripts from being executed at a unknown untrusted domain. If you are familiar with Java Applet, here is a similar analogy: in an applet application, you are literally prohibited from making connections to the outside world differing from your own domain.

In this blog, I will try to explain a way to allow remote scripting with BlogSpot and certainly you can use it with other web application as well.

First attempt: Load the scripts directly (failure)

so you include the remote scripts with the following statements:

<script type="text/javascript" src="http://remote-domain/test.js"/>

And then when you try to call a method in that test.js script, the browser chokes complaining permission denied.

It doesn't work because we can't fool CDS with this trick!

Second attempt: request the remote scripts then load them programmatically (failure)

This time, I try to get the scripts from my local script then attempt to load them in memory using the built-in eval method of javascript 1.4. here is how it works:

<script>

var req;
function loadJS(url)
{

req = false; // branch for native XMLHttpRequest object
if(window.XMLHttpRequest)
{
try { req = new XMLHttpRequest(); } catch(e) { req = false; }

// branch for IE/Windows ActiveX version }
else if(window.ActiveXObject)
{
try
{
req = new ActiveXObject("Msxml2.XMLHTTP");
}
catch(e)
{
try
{
req = new ActiveXObject("Microsoft.XMLHTTP");
}
catch(e) { req = false; }
}
}
if(req)
{
req.onreadystatechange = insertJS;
req.open("GET", url, true); req.send("");
}

}

function insertJS()
{
var code = req.getResponseText;
eval(code);
}
<script/>

So you call this method loadJS('http://remote-domain/test.js') and hope it works. No it doesn't! The same error "permission denied" will popup again.

Third attempt: request the remote scripts then define inline scripts using those script (failure)

So I use the previous code to request the scripts from different domain as above. But this time, instead of using the eval method, I create a DOM node to build an inline script tag:

function insertJS()
{
var code = req.getResponseText;
var sNode = document.createElement('script');
sNode.setAttribute('type','text/javascript');
sNode.appendChild(document.createTextNode(code));
}

The browser (IE) chokes at the line
sNode.appendChild(document.createTextNode(code));
Microsoft is smart enough to check that if the element you create has a tag named "script" then you cannnot alter its text value!

A little worthy note here: instead of trying to change the script's text node as above, you can include the script using the following:

sNode.setAttribute('scr',url-to-script);

But this approach brings us back to the first (failure) attempt ! You can use it to dynamically include a serries of JS files though.

Fourth attempt: loading the script remotely (successful!)

So I use something similar to the first attempt. But this time, I change the file extension to .html instead of using .js
Secondly, I concatenate all the scripts into a big-ass file (which I accidentally called bigass.html Tongue out ).
Loading the script as normal:

<script type="text/javascript" src="http://remote-domain/bigass.html"/>

Now everything works like a champ.

I setup a blog call Random hack to test my hack. You can visit the blog to see the hack as I use prototype framework, scriptaculous to create special effect on the page. For the moment, something strange may happen as I change my code everyday.

I do not know for sure why or how it works yet. If you know, please post a comment so everybody could learn.

happy Coding

No comments: