Tuesday, February 8, 2011

Data URIs & window.open()

I recently had the requirement, to open base64 encoded images in a new window. "Easy task" I said to myself, and just passed in the base64 encoded image source to window.open(). This works, since the .open() method can handle pretty much everything a browser can display. See MDC Doc Center.

However, this approach has a major downside. All the image data information goes right into the browsers address bar! Whaeerrhhg, not only this looks terrible, it will also slow down the action if you try to open some "big" base64 encoded images with this technique.

Example:


(You can execute a code snippet, by right-clicking it)

window.open("data:image/png;base64,R0lGODlhDQAOAJEAANno6wBmZgAAAAAAACH5BAAAAAAALAAAAAANAA4AQAIjjI8Iyw3GhACSQecutsFV3nzgNi7SVEbo06lZa66LRib2UQAAOw%3D%3D", "large");

So that was not an option, because I had to deal with lots of big images.
My idea was, not to pass an image data-uri directly, but to pass a data:text/html; string into window.open(), to magically create a little DOM tree in the newly created window. Looks like:

var largeprev = window.open('data:text/html;charset=utf-8,' + escape('
Hello, I was created through a data uri
'), 'large');

As we all know, window.open() returns a DOMWindow object. This reference can be used to access / modify the contents of that window (only restriction, the SOP (Same Origin Policy) will take effect here!)

So my plan was to do something like this:

jQuery('', {
    src: "data:image/png;base64,R0lGODlhDQAOAJEAANno6wBmZgAAAAAAACH5BAAAAAAALAAAAAANAA4AQAIjjI8Iyw3GhACSQecutsFV3nzgNi7SVEbo06lZa66LRib2UQAAOw%3D%3D",
    click: function() {
        var largeprev = window.open('data:text/html;charset=utf-8,' + escape('
'), 'large'), that = this; largeprev.addEventListener('DOMContentLoaded', function(e) { largeprev.document.querySelectorAll('div')[0].appendChild(that); }, false); } }).prependTo(document.body);

I could not wait to test the code. Opened Firefox (3.6.x.) and executed.. it worked great! Woohoo! Test it yourself here

I was pretty confident that this will work cross browser (at least, in all browsers that support Data-URIs), but to my shock neither Chrome(9) nor Safari (5) were able to run the above code.

The problem is, Webkit seems to treat the returned DOMWindow, like a foreign domain document. In other words, Chrome and Safari apply the SOP restrictions to a window, that was created with a Data-URI. AFAIK, a data: resource should inherit the "document.domain" from the environment in which it was encountered. I've sent a request for clarification to the whatwg already.

For now, the only working cross-browser solution, is to invoke document.write() like so:

jQuery('', {
    src: "data:image/png;base64,R0lGODlhDQAOAJEAANno6wBmZgAAAAAAACH5BAAAAAAALAAAAAANAA4AQAIjjI8Iyw3GhACSQecutsFV3nzgNi7SVEbo06lZa66LRib2UQAAOw%3D%3D",
    click: function() {
        var virtualdom = '',
            prev       = window.open('', 'large');

        prev.document.open();
        prev.document.write(virtualdom);
        prev.document.close();
    }
}).prependTo(document.body);

The above code does not get rendered correctly. The virtualdom variable looks different, see the code in Action.

---

Update:

I was just informed by a whatwg member, that there is an open ticket for Chrome at least:
http://code.google.com/p/chromium/issues/detail?id=58999

So, I guess this will get fixed in future versions.

1 comment:

  1. All the fuzz for this?

    var d = window.open().document;
    d.write(''); d.close();
    d.body.appendChild(document.createElement('img')).src = 'data:image/png;base64...';

    ReplyDelete