postMessage giving "Blocked frame" error, but only in Safari

Viewed 2619

I am using postMessage to share data between https://www.example.com (Which I'll call mainSite) and https://subdomain.example.com (which I'll call subSite)

subSite has an iframe to mainSite,

subSite's code looks something like:

//set the src of the iframe
$("#main-site-iframe").attr("src", "https://www.example.com");
//wait for the main site page to open
$("#main-site-iframe")[0].onload = () => {
    //my page posts a message to their site
    $("#my-iframe")[0].contentWindow.postMessage("messagePlox", "https://www.example.com");  
};

//wait for the iframe to message back
window.addEventListener('message', iframeResponse, false);
function iframeResponse(e) {
    //make sure the request is from the correct site
    if(e.origin == 'https://www.example.com')
    {
        //Got the data
        console.log(e.data);
    }
}

mainSite looks like this:

//listen for the subdomain to make a request
window.addEventListener('message', subdomainRequest, false);
function subdomainRequest(e) {
    //make sure the request is from the correct subdomain
    if(e.origin == 'https://subdomain.example.com')
    {
        //respond with the data
        e.source.postMessage("We got you", e.origin);
    }
}

The problem with the above is that it works in every browser EXCEPT Safari which refuses it and says:

Blocked a frame with origin "https://www.example.com" from accessing a frame with origin "https://subdomain.example.com". Protocols, domains, and ports must match.

Which seems to imply the response back is breaking since mainSite is trying to "access" subSite.

Does anyone know why this would only happen in Safari and not in Firefox or Chrome?

1 Answers

Two-way cross-domain iFrame communication is usually blocked in Safari/Opera. The primary way around this is using a gateway that the parent and child both agree on... but they cannot using IPC use network-less messages to communicate bidirectionally. Alternatively, There are some exceptions that are closely guarded by the Nitro JS engine team, it appears if you have a large iFrame and the user is interacting with it, the messages from the parent will go through, and vise versa.

Messages that come from the parent get handled as if they are in a SW... that is to say don't expect things to work normally in the handler... test a lot. My promise resolve function for instance, can't be called from a message handling function for some reason... Which means I have to check to see if the function has been called in a time out, making things, painfully slow.

If you just need to show a success message from the child, you could do this:

parent.sendSuccessMsg();

function sendSuccessMsg(){
    document.getElementById('iframeID').src="http://example.com/?successData=123";
}

Otherwise, you can use websockets with a gateway to mingle cross-domain. There seems to be some sort of undocumented exception if you load the iFrame twice, and the message is sent early on iframe load.

Source: https://benohead.com/blog/2015/12/07/cross-document-communication-with-iframes/ Bug/Failsafe Discovery: how to send cross domain post message in safari?

Related