IntroductionFlash code embedded in a web page with allowScriptAccess="all" can execute arbitrary Javascript code, violating Caja invariants. Third party flash should therefore only be included in a page if it has allowNetworking="internal" set, which prevents the Flash code from modifying browser state (i.e. the Flash code can't redirect the page, cause the browser to fetch images or scripts, or interact with Javascript code). The Flash bridge is trusted code that mediates the interaction between the Flash code and the cajoled Javascript code running in the browser. FlashExisting third-party Flash will need to be rewritten to interact with the bridge. The third-party Flash will have a channel parameter that tells where the bridge is listening and sending data. The bridge listens on channel + 'js' and sends on channel + 'swf'. class Example extends MovieClip {
var lcJS, lcSWF;
// This function is called from JavaScript,
// does a computation, and sends the result
// back to JavaScript
function mySWFMethod(callbackName, data) {
// Do some stuff.
...
// Now invoke a JavaScript function with the result
if (!lcJS) { lcJS = new LocalConnection(); }
lcJS.send(channel + 'js', 'callJS', callbackName, result);
}
function onLoad() {
// Listen for messages sent from the bridge
lcSWF = new LocalConnection();
lcSWF.connect(_root.channel + 'swf');
lcSWF.mySWFMethod = mySWFMethod;
}
}Host pageThe host page defines two Javascript functions, callJS and onFlashBridgeReady. callJS = function (functionName, argv) {
// This assumes that there's a single gadget in the frame.
var $v = ___.getNewModuleHandler().getImports().$v;
// Invoke the cajoled version of the function
return $v.cf($v.ro(functionName), [argv]);
};
onFlashBridgeReady = function () {
// Pass the message to the cajoled code
callJS("onFlashBridgeReady");
};Objects passed to Flash code must be directed and acyclic. Since Caja modifies Object.prototype in such a way that no object satisfies that rule, we must clone the objects using a fresh Object and Array constructor: var clone = (function () {
// Gets a fresh Array and Object constructor that
// doesn't have the caja properties on it. This is
// important for passing objects across the boundary
// to flash code.
var ifr = document.createElement("iframe");
ifr.width = 1; ifr.height = 1; ifr.border = 0;
document.body.appendChild(ifr);
var A = ifr.contentWindow.Array;
var O = ifr.contentWindow.Object;
document.body.removeChild(ifr);
var c = function(obj) {
var t = typeof obj, i;
if (t === 'number' || t === 'boolean' || t === 'string') { return obj; }
if (t === 'object') {
var o;
if (obj instanceof Array) { o = new A; }
else if (obj instanceof Object) { o = new O; }
for (i in obj) {
if (/__$/.test(i)) { continue; }
o[i] = c(obj[i]);
}
return o;
}
return (void 0);
};
return c;
})();The host page exposes the callSWF function of the bridge to the cajoled code: outers.bridge = ___.primFreeze({
callSWF: (function (channel) {
return ___.func(function (methodName, argv) {
return bridge___.callSWF("" + channel, "" + methodName, clone(argv));
});
})(channel);
});In an OpenSocial environment, the object above is returned from the call to embedFlash. JavascriptInvoking Flash code from cajoled Javascript code is simple: bridge.callSWF("mySWFMethod", argv);
|