Friday, January 25, 2013

document.domain and iframe parent talk 101

Alleyoop relies on partner for a lot of its content which we embed in an iframe.
For some videos, we do the hosting outselves, and so we have a standalone Grails app that "pretends" to be a partner, just to keep everything nice and honest. 

This current iteration involves upgrading that app so it can embed quiz questions in a sidebar. We're interested in how students respond to the new feature, so we have to add analytics inside the "partner" app.

I began copy and pasting the subset of code for analytics when I realized it wasn't as easy as it looked, our core analytics do some checking to see if this looks like a QA or developer whose data shouldn't "corrupt" the core stats, so the partner app would have to start to know way too much about the core app's user model and what not. (Especially challenging given how we deploy to Amazon's cloud service, so URLs might not be well known at coding time.)

With that in mind, I realized it would be easier and safer to create an analytics passthrough in the main app so that the "partner" app can talk through it, relying on the parent app to do all the heavy lifting.

The system relies on the fact that both the main app and the "partner" app live on the same domain (albeit on different subdomains) -- without that we'd probably be out of luck with this approach, because of the security restrictions on cross domain scripting.

So as a note to my future self, or other people doing something similar:

To start with, I had to update /etc/hosts so that the server on localhost could be called via the alleyoop.domain:
127.0.0.1   grails.alleyoop.com

Then as a Proof of Concept, I made the following iframe in the parent app:
<script>
document.domain = "alleyoop.com";
function gotIt(){
    alert("!!!got it!!!!");
}
</script>

<iframe width="100%" height="100%" 

src="http://grails.alleyoop.com:8888/AlleyoopVideoPartner/">
</iframe>


Then in the "partner" app, near the top:
<script>
    document.domain = "alleyoop.com";
    window.parent.gotIt();
</script>


And that worked. "document.domain" is kind of the secret sauce, it has to be the same if the two pages are going to communicate. Note that you can't set "document.domain" to whatever you want, it is constrained by the domain where the page is actually living.

I'm glad it worked! My Plan B was a terrible cookie hack and poller...

BONUS PROTIP: A few days later and I ran into the above code not working... it was because of an http/https mismatch in the page and the content of the iframe. If I wasn't so tired it wouldn't have taken me so long to figure out!

No comments:

Post a Comment