1
votes

I have a swf, loaded into the non-application sandbox in Adobe AIR 1.5 (the shell is already installed with our users so I can't update to version 2+).

On the stage in the swf are buttons, movieclips, animations etc - all of these work fine.

When we add an input TextField, selecting this TextField causes a Security Sandbox Violation.

Error message (in debug mode) is (I've edited the actual file names):

[trace] *** Security Sandbox Violation ***
[trace] SecurityDomain 'file:///path/to/local/loaded.swf' tried to access incompatible context 'app:/loadingApp-debug.swf'

The user then is unable to enter text into the TextField. The rest of the application is unaffected.

The FDB stacktrace only shows:

this = [Object 57216577, class='flash.utils::Timer'].Timer/tick() at <null>:0

Has anyone got a workaround for this?

I'm guessing it's either the TextField attempting to access the stage, or an event attempting to bubble / access global properties.

I understand the air sandbox restrictions and use them daily - with sandboxBridges from parent to child and child to parent etc - perhaps there is something I need to expose to allow this to work?

Any clues?

Edit:

I've now tracked down the problem to being that the TextField attempts to do

this.stage.focus = this;

or something equivalent when MouseDown happens.

It also appears that there is no access to KeyboardEvents in loaded swfs, so my thought of making the 'field' a button and then controlling input by listening to KeyboardEvents is dead in the water.

Now looking at whether to relay events to callbacks passed through the parent sandbox bridge, or whether minimal comps might save my butt.

1

1 Answers

2
votes

Ok, I have an insane workaround, but it's pretty solid. I'm going to post it almost in full here, though I'll probably make it generic and upload it to github at some point.

In my shell, I have a view-with-mediator (I'm using robotlegs) which I'm calling EventRelayer and EventRelayerMediator.

The view's only purpose is to give the mediator access to the stage.

I exposed some functions on the parentSandboxBridge:

public function requestKeyboardEventRelay(eventType:String, callback:Function):void;

public function requestMouseEventRelay(eventType:String, callback:Function):void;

public function cancelKeyboardEventRelay(eventType:String, callback:Function):void;

public function cancelMouseEventRelay(eventType:String, callback:Function):void;

My sandbox bridges always just translate into strong typed events, so these fire events like:

RelayEvent(RelayEvent.START_RELAY_REQUESTED, KeyboardEvent, eventType, callback);

RelayEvent(RelayEvent.CANCEL_RELAY_REQUESTED, MouseEvent, eventType, callback);

These are picked up by the EventRelayerMediator, and translated into handlers in an eventMap:

override public function onRegister():void
{           
    createRelayHandlerFactories();
    eventMap.mapListener(eventDispatcher, RelayEvent.START_RELAY_REQUESTED, startRelay);
}

protected function startRelay(e:RelayEvent):void
{
    var handler:Function = createRelayHandler(e.relayEventClass, e.callback);
    eventMap.mapListener(view.stage, e.relayEventType, handler, e.relayEventClass);
}

protected function createRelayHandler(relayEventClass:Class, callback:Function):Function
{
    var handler:Function = relayHandlerFactoriesByEventClass[relayEventClass](callback);

    return handler;
    }                  

protected function createRelayHandlerFactories():void
{
    relayHandlerFactoriesByEventClass = new Dictionary();
    relayHandlerFactoriesByEventClass[KeyboardEvent] = createKeyboardEventRelayHandler;
    relayHandlerFactoriesByEventClass[MouseEvent] = createMouseEventRelayHandler;
}                                                                                      

protected function createKeyboardEventRelayHandler(callback:Function):Function
{
    var handler:Function = function(e:KeyboardEvent):void
    {            
        trace("Relaying from shell: " + e.toString());
        // passing an object because the sandbox bridge doesn't allow strong typed values, only primitives
        var o:Object = {};
        o.type = e.type;
        o.charCode = e.charCode;
        o.keyCode = e.keyCode;
        o.altKey = e.altKey; 
        o.ctrlKey = e.ctrlKey;
        o.shiftKey = e.shiftKey;
        // no point adding other props as we can't pass them
        // to the constructor of the KeyboardEvent
        callback(o)
    } 

    return handler;
} 

The loaded swf passes a callback which just re-assembles and re-dispatches the events.

My input TextField is now just a dynamic field with a click handler that activates listening for keyboard events on the root of the swf, and then updates the dynamic field accordingly.

At the moment that is super-crude but I'll break it out into a robust, tested class now I know it works.

I've used a dictionary to manage the handlers because I'm sure that memory leakage hell is about to follow and I'm expecting to have to relay the FocusEvents to stop entering text.

I need to test memory leakage, return a binding from the parentSandboxBridge function so that I can make sure I don't add the same handler twice etc etc, but Adobe - you suck for not calling this out and providing a built in relay mechanism.