Saturday, August 4, 2007

crossbrowser HTTP requests with document.domain make ExtJS grid garbled and unhappy

Not much of SL this week. Why ? because most of my time is dedicated at the moment to playing with the ExtJS. They've just released the 1.1 version (which I am to try out yet).

Even 1.0.1 looked very promising - all the standard UI elements, crossbrowser and without much pain.

Although one thing I spent a lot of time on, was a very cute bug. In my little experiment, I need to make cross-domain XMLHTTPRequests, so I obviously needed to set the document.domain to be able to do that. Unfortunately, this caused the grid element to become garbled in FF-derived browsers - it looked as if the columns did not have width (no padding). Took a while to figure out.

Eventually I solved it by a following hack (which is known also as an iframe bridge hack):


main window (server X) loads an iframe from server Y. This is my "main window", so I clean up the DOM in the main window, and make sure the iframe sits in the whole screen. this iframe then loads another iframe, also from server Y.

Within the child iframe there are a bunch of functions that call the functions in the main window.

Now, the trouble is that either the main iframe can call these functions, or these functions can call the main window - because of the different document.domain properties.

So, the trick is to initialize the variables in the main iframe with "pointers" to functions in the child iframe, and then call the initializer in the child iframe to set the document.domain.

This way the main iframe still "remembers" about the functions.

Now, there is another trouble. When making XMLHTTPRequest, you supply the callback. And javascript does really wonders there. But... the problem is that it does not work all that well - I was getting an error in mozilla.

So, the trick is to treat the callback as an "opaque blob" inside the child frame, and perform the same trick with the functions in the other direction - in the main iframe, create a function that takes a callback as an argument and calls it.

So, overall it looks something like it:


[ main window ]

document.domain = "example.com";

function parentFunc(url, cb) {
makeAjax(url, cb);
}

[ main iframe -> its html loads the child iframe ]

function CallIt(cb, result) {
cb(result);
}

var bridgeFunc;

function init() {

bridgeFunc = document.frames[0].childFunc;
// that one also sets the document.domain
document.frames[0].init(CallIt);
}

function myMakeAjax(url, cb) {
bridgeFunc(url, cb);
}


[ child iframe ]

var parentCall;

function init(bridgeFunc) {
parentCall = bridgeFunc;
// now we would not be able to talk directly except via bridge variables.
document.domain = "example.com";
}

function childFunc(url, cb) {
window.top.parentFunc(url, function(result) {
parentCall(cb, result)
});
}

----
With this code (indeed in real world it looks bit more complex than this ), the document.domain in my main iframe window never changes, and as such, I can enjoy the beauty of the ExtJS's controls. So, firefox also has its annoying bugs :) (or is it a security feature ?)

No comments: