My favorites | Sign in
Logo
                
Search
for
Updated Jul 24, 2009 by metaweta
Labels: flash
FlashBridge  
Using the Flash bridge.

Introduction

Flash 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.

Flash

Existing 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 page

The 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.

Javascript

Invoking Flash code from cajoled Javascript code is simple:

bridge.callSWF("mySWFMethod", argv);

Sign in to add a comment