The AIR platform defines distinct sandboxes for trusted and untrusted code, and provides a way to talk securely between each sandbox via sandbox “bridges”. This is a lynch-pin in the web-meets-desktop strategy that AIR embodies, but it can also present some of the trickier development challenges, with plenty of head-banging opportunity. I’ll share a few tips to help you avoid those head/keyboard collisions.

The Problem

Any content downloaded from the web is inherently tainted and untrustworthy. Any code running from outside the application directory is blocked from accessing AIR APIs. Nor can you write to the application directory at runtime and load up a downloaded page as a part of your application – with all the system access that implies. That would be bad. But, what if you wanted to make an offline viewer of some of your web content? You can download the files, write them to your application storage directory and load them up into an iframe or nativeWindow, and happily browse them in the bus/train/plane/smallest-room. Making a window or iframe for that content is trivial, but to make a compelling and seamless experience you’ll eventually need to pass events to and from the application and the offline content – and that means live, secure, runtime communication across the two sandboxes. You need sandbox bridges.

Bridging the Sandboxes

Adobe has provided quite a bit of documentation about security in AIR. See this Security FAQ and this entry on the Sandboxes for starters. As a developer, you can create an explicit interface that places functions or objects on the sandbox “bridge” – which appears as a new window property. The process is well outlined in this quickstart guide, so I won’t duplicate it here. Here’s the short version, imagine we’ve got our main application window (in the application sandbox), and a child iframe (non-application, network sandbox).

// make a function on the parent available to the child window
window.parentSandboxBridge.receiveSomething = function(arg) {
  alert("the child sent me something: " + arg);
}

And in our child window:

// make a function on the child window available to the parent
window.childSandboxBridge.receiveSomething = function(arg) {
  alert("the parent sent me something:", arg);
}

To call them: from the application window

myIframe.contentWindow.childSandboxBridge.receiveSomething(5);

..And from the child:

parent.parentSandboxBridge.receiveSomething("a string");

The two sandboxBridge objects already provide some measure of protection by ensuring intentionality — there’s no ad-hoc access to content in other sandboxes — everything must get channeled through the bridge. There are some gotchas though that require a little more understanding of how the bridge works. Think of it like two prisoners in adjacent cells, with a sturdy iron grill set into the wall between them. They can talk, but pass nothing potentially dangerous to each other. Strings fit just fine through the bars, so do Numbers. Objects and Arrays also fit, but their contents have to meet the same conditions; content passed across the bridge is serialized and de-serialized. That doesn’t sound terribly problematic until you consider what gets blocked: Date objects, RegExp objects, Errors – the list goes on. If its not an Array, Object or one of the basic primitive object types, it’s likely to come out the other end as a neutered object. At first glance it might appear that you can pass DomNodes across. innerHTML works, so does tagName. But these are just more examples of string properties; if you attempt to call getElementsByTagName() on what gets passed across, it will fail.

The result – and the goal from Adobe’s point of view – is that you have to think about what you pass between sandboxes. I’ve explored how values get passed across the sandboxBridge, and provided some reference code for setting it all up in a tester AIR app, downloadable at the end of this article. Examine the code carefully and compare to the result values, and add and change tests to get comfortable with what’s coming across.

Event Sequence

The other gotcha that’s waiting for you is one of timing. Say you have an iframe thats loading content from a non-application sandbox, but you want to get notification of events in the parent window: Code in the child window needs to be able to rely on functions on the parentSandboxBridge being defined already as its interpreted, and code in the parent window needs be able to call functions on the childSandboxBridge when they are ready, but not before. A new iframe attribute ondominitialize makes it possible – it gives you a hook to do setup work in the parent page before any of the script elements in the iframe’d document have even been read. Note that its not a window event, its an iframe attribute – it can be set once from the parent, without the loaded document having to know about it – and it will fire whenever a new document is about to load.

SandboxBridging in Practice

Here’s a couple examples from the Dojo Toolbox where we put it into practice:

API Viewer Screenshot
In the Dojo Toolbox’s API Viewer – we browse downloaded content, drop some files alongside it and talk across the bridge to signal things like loaded events. When the child document is loaded, this event is passed to the parent which manipulates the history array/pointer accordingly.

Builder Screenshot
In the Dojo Toolbox’s Builder – we ask the user what directory to conduct the build in, and the build process needs the ability to load and eval code, so it must be done from a local sandbox. We build an iframe with the required url and set ondominitialize to point at the _setupBridges method of our module. That method can run knowing that iframe.contentWindow is available, and set up the parentSandboxBridge so that when the child document loads, it is immediately available and in scope. When both parties are ready, the module (application sandbox) can call a method in the child to start the build. Progress indication is a function of the module, but the child needs to provide updates, so it does so via a method on the parentSandboxBridge.

The iframe creation code ends up looking something like this:

var html = '';

document.getElementById("icontainer").innerHTML = html;

Setting the sandboxRoot to a non-app:/ hostname/url puts content in my iframe in a network, non-application sandbox – even though in this case its technically loaded from the application directory. I create the iframe dynamically because a) Sometimes you need to, and b) because it gives me the full range of before/during/after events around the window creation.

Summary

Let’s review. You don’t need to worry about sandbox bridges if:

  1. All your content is built into the installer, and available under the app:/ root (i.e. the directory you install into, or when testing, the directory containing the application decscriptor file.)

You do need to use the sandbox bridges if:

  1. You want to load up content that was not a part of your application as installed – be it from the storage directory, or out there on the web – AND – you want some to access select parent-window properties and functions from the child (parentSandboxBridge) or child-window properties from the parent (childSandboxBridge)
  2. You have code that uses eval, or the new Function("statements as string") technique.

Here’s what you can expect to get across the bridge unmolested:

  • Strings, Numbers
  • Objects, Arrays – though their contents must also pass the same tests
  • Functions (!) my tests show you can pass a function across the bridge, though you may lose the intended scope, however simple closures seem to work.

You’ll run into problems with:

  • Date objects
  • RegExp objects
  • Any kind of AIR/Flash objects
  • DOM Nodes

Finally

The security sandboxes – and the bridging facility across them – make possible some sophisticated interactions across application and non-application code. They are at once a PITA and a power-tool. They allow you as the developer to establish degrees of trust rather than just a good/bad determination, and provide a safe api to remote content.

Please download the sandbox tester code (HTMLSandboxBridge.zip) here. Run as usual with adl HTMLSandboxBridge/application.xml. This code builds on and owes a credit to Jeff Schwartz’ HTMLSandboxBridge, available from his QuickStart article mentioned earlier.