Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Detect if browser supports data uri scheme with iframes

Internet Explorer does not support the data uri scheme for iframe urls (see http://msdn.microsoft.com/en-us/library/cc848897%28v=vs.85%29.aspx). Other browsers do. As browser detection is loaded with testing and future-proofing problems, I want to use feature detection to work around this issue.

So: how can I detect whether or not a browser supports the data uri scheme for iframes?

like image 430
Paul Taylor Avatar asked Aug 04 '14 16:08

Paul Taylor


2 Answers

This solution by Kevin Martin is tested and seems to be giving the correct result in IE, FF and Chrome:

function iframeDataURITest(src) {
    var support,
        iframe = document.createElement('iframe');

    iframe.style.display = 'none';
    iframe.setAttribute('src', src);

    document.body.appendChild(iframe);

    try {
        support = !!iframe.contentDocument;
    } catch (e) {
        support = false;
    }

    document.body.removeChild(iframe);

    return support;
}

console.log('Empty data uri', iframeDataURITest('data:;base64,'));
console.log('"*" data uri', iframeDataURITest('data:text/html;base64,Kg=='));

Unlike some of the other suggestions, it is synchronous - no need to mess around with timeouts or callbacks.

like image 135
Luna Avatar answered Sep 28 '22 00:09

Luna


If the onload event of the iframe with a data: URI fires, the browser supports data: URIs. Otherwise, the browser doesn't support data: URIs.

The example code also checks if scripting is allowed from a data: URI by sending a message from the iframe to the parent window.

Working code

var iframeDataURISupport = { checked: false, supported: false, scriptingSupported: false };

function iframesSupportDataURIs(callback) {
    if (!iframeDataURISupport.checked) {
        var iframe = document.createElement('iframe'), alreadyCalled = false, done = function () {
            if (!alreadyCalled) {
                alreadyCalled = true;

                document.body.removeChild(iframe);

                console.log(iframeDataURISupport);

                callback && callback(iframeDataURISupport);
            }
        }, previousMessageHandler = window.onmessage, dataURI = 'data:text/html,<' + 'script>window.parent.postMessage("data: URIs supported", "*");<' + '/script>';

        window.onmessage = function (e) {
            if (e.data === 'data: URIs supported') {
                window.onmessage = previousMessageHandler;

                iframeDataURISupport.supported = true;
                iframeDataURISupport.scriptingSupported = true;

                done();
            } else {
                window.onmessage.apply(this, arguments);
            }
        };

        iframe.src = dataURI;
        iframe.setAttribute('style', 'display: inline-block; width: 0; height: 0; overflow: hidden; border: 0 none; padding: 0; margin: 0;'.replace(/;/g, ' !important;'));

        iframe.onload = function (e) {
            if (iframe.src === dataURI) {
                iframeDataURISupport.supported = true;

                setTimeout(done, 100);
            } else done();
        };

        document.body.appendChild(iframe);

        setTimeout(done, 500);
    } else {
        setTimeout(function () {
            callback && callback(iframeDataURISupport);
        }, 5);
    }
};

Usage

iframesSupportDataURIs(function (details) {
    alert('This browser ' + (details.supported ? 'supports' : 'does not support') + ' data: URIs. It ' + (details.scriptingSupported ? 'also supports' : 'does not support') + ' scripting from data: URIs');
});

If you want more advanced control, you can call it like this:

iframeDataURISupport.checked ? functionName(iframeDataURISupport) : iframesSupportDataURIs(functionName);

Demo

Play with it on JSFiddle.

like image 37
Toothbrush Avatar answered Sep 28 '22 00:09

Toothbrush