Sandbox telegrams, or, how your Chrome extension can interact with page content scripts

In AdGrok’s GrokBar, we inject a “heads-up display” on pages that the user is advertising. The heads-up display is actually an iframe that’s positioned within a browser-extension-injected div, and that iframe renders content from our secure server farm. You can see a demo video here (and see that we truly spared no expense on the voice-over talent!).

I wanted to make our extension’s button-click incant a javascript method that was inside the GrokBar’s iframe. I found this horrible hack, but every time a javascript timer scrapes a hidden DOM element, or mucks with the URL fragment of an iframe src in order to send messages, the code gods kick a puppy.

Just executeScript?

So I first tried to use executeScript, but by default, executeScript only runs against the tab’s topmost main frame. By using the allFrames option, you can get your executeScript to run inside the iframe, too.

I knew about Chrome’s isolated worlds, but I didn’t realize that extensions are considered a different world from page content scripts — my executeScript failed from “unknown method.”

HTML5 postMessage?

I then thought to use postMessage from within the extension, but Chrome’s sandboxing prevents access to an iframe’s window object, which is what you’d incant the postMessage against, from anywhere except in the scope of the iframe. Whether this is a bug or a design decision, I don’t really care — the jist is that it doesn’t work.

You got your postMessage in my executeScript! Delicious!

But… what if you combined them?

Call postMessage with a data payload, against itself, using executeScript. A previously established message listener within the iframe, and within the context of the javascript sandbox, could then receive the postMessage and dispatch whatever method was necessary. No nasty DOM scraping! woot! The puppies are saved!

So, within the chrome extension, we call:

Assume that ADGROK.server() returns something like https://secure.adgrok.com, so we know the postMessage will only run on the iframe we want it to.

Within the iframe, we have this javascript code:

Posted in Technical HOWTOs Tagged with: , ,
  • http://www.openzki.com/firewall Firewall Implementation

    Perfect, everything under one roof! Thanks for this post…

  • http://canonical.org/~kragen/ Kragen Javier Sitaker

    Hm, that’s interesting. It took me a while to understand the problem, but now I do. From the Twitter reactions, it looks like I wasn’t the only one — if you only wanted to send a message to a page content script, as the Tweets say, you could just use chrome.tab.sendRequest(), but you’re actually wanting to talk to server-sent JS inside the iframe instead.

    You might want to check the origin of the message in your on_message handler.

  • Anonymous

    Excellent point — I’ll update the code to check the caller (I was already doing that for the firefox 3 port of the grokbar…)

  • Anonymous

    Awesome, I tried to do this via localStorage, with the native ‘storage’ event.
    As both the window and the safebox window share the localstorage as it turns out.
    But ‘storage’ even only fires on when a different window alltogether.The problem I had with your method, is what happens  when I want to talk with the content script from the page? I mean the other way around. How would I go about it?

  • Anonymous

    Due to the “isolated worlds” architecture, the page scripts and the extension-injected scripts can’t talk directly to each other. You can use postMessage directly to the same window, though, just like what is described here, except in your case, without using executeScript to dispatch the event.