Flash SecurityDomain, P2P settings and multiply swfs - actionscript-3

I have two swfs:
application swf
p2p client swf that allows to load data using rtmfp replication
technology (through cirrus service)
The main idea is to have one p2p loader on the certain domain that will able to work in p2p network without asking many times for permission for each domain, so for instance:
application 1 (http://domain1.com/app.swf) |
application 2 (http://domain2.com/app.swf) | <--> p2p data loader (http://domainp2p.com/p2pcli.swf)
application N (http://domainN.com/app.swf) |
The p2p client loads binary data by request, content really does not matter I believe.
So, I load p2pclient swf using following class (app.swf)
public class ClientLoader {
// .. some code
public function load(cb:Function, err:Function):void
{
_cb = cb;
_err = err;
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, _onLoaded);
loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, _onIoError);
loader.contentLoaderInfo.addEventListener(SecurityErrorEvent.SECURITY_ERROR, _onSecurityError);
// note that context has neither application domain nor security domain
loader.load(new URLRequest(_url), new LoaderContext());
}
private function _onLoaded(e:Event):void
{
trace("Loaded. Connecting to the p2p network...");
_client = e.target.content;
_client.addEventListener(Event.CONNECT, _onClientReady);
_client.connect();
}
private function _onClientReady(e:Event):void
{
_cb(_client);
}
}
}
The p2pclient itself (p2pcli.swf):
public class P2P extends Sprite
{
public function SqP2P() {
Security.allowDomain("*");
}
public function connect():void
{
_connection = new NetConnection();
_connection.addEventListener(NetStatusEvent.NET_STATUS, _netStatus);
_connection.connect(CIRRUS_ADDRESS, CIRRUS_KEY);
// after successful connect this method called
_loadGroup();
}
private method _loadGroup():void
{
var spec:GroupSpecifier = new GroupSpecifier(_name);
spec.serverChannelEnabled = true;
spec.objectReplicationEnabled = true;
_group = new NetGroup(connection, spec.groupspecWithAuthorizations());
_group.addEventListener(NetStatusEvent.NET_STATUS, _netStatus);
}
private function _netStatus(event:NetStatusEvent):void
{
trace("NetStatusEvent:", event.info.code);
}
}
But it looks like that Flash Player ignores the security session and is trying to save the pop-up settings for the domain that app.swf belongs to, but not for the p2pcli.swf domain. Why?!
I have absolutely same code, but p2pcli.swf replaced with swf that stores data in Local Shared Object and all the domain1-2-N.com has access to it.
Any ideas?
I know, my English is crap :(

I really am not totally sure, but I'll throw my answer out there in case it is helpful.
Based on the general purpose of such security messages, I'm not entirely sure you CAN keep that dialog from showing up. In some cases, I'm certain Peer Assisted Networking can be a security risk for some people (and anyway, it is using their bandwidth.) The settings for turning on and off that notification are user-side, in the Flash settings dialog (Control Panel in Windows 7...), thus that hints that it is inherently hardwired into the Flash platform.
Of course, since I'm more of an Adobe AIR specialist, I could be completely wrong...for your project's sake, I'm sincerely hoping I AM!
And, for the record, your English was almost perfect. I tweaked one paragraph for clarity, but otherwise, spot on. :D

Related

AS3 using Workers

IDE: FLASH CS6 Professional
SDK: AIR 18 for Android
I'm trying to use Workers for my app.
The problem is, it doesn't work that it suppose to be.
public class Main extends Sprite {
private var worker:Worker;
public function Main() {
var ba:ByteArray = this.loaderInfo.bytes;
if(Worker.current.isPrimordial){
worker = WorkerDomain.current.createWorker(ba);
worker.start();
trace("Created Main");
}else{
trace("Created Worker");
}
}
}
It should output
Created Main
Created Worker
but i'm only getting
[SWF] Test.swf - 2831 bytes after decompression
Created Main
[UnloadSWF] Test.swf
Test Movie terminated.
EDIT:
Ok i tried to publish the app and ran it. It works like it should be. But why doesn't it work when i try to ran it with Adobe Debug Launcher ?
This is what it looks like on Android Debug Launcher (ADL)
ADL
This is what it looks like when Published.
Published
EDIT 2:
Tried it with Flash Professional CC 2015. Upgraded to AIR SDK 19.0.0.213.
Still the same result. What a pain Adobe.
It should work, but Flash Pro CS6 doesn't show the worker's trace statements. Check flashlog.txt or send messages from the worker to the main SWF over a MessageChannel.
I made a lib to support use of Workers, if you want use you can check it in GitHub, it is open source project.. hope help ASWorker Link
The implementation is like this
package
{
import com.tavernari.asworker.ASWorker;
import com.tavernari.asworker.notification.NotificationCenter;
import com.tavernari.asworker.notification.NotificationCenterEvent;
import flash.display.Sprite;
public class ASWorkerDemo extends Sprite
{
private var asWorker:ASWorker;
//BOTH AREA
public function ASWorkerDemo()
{
//important know, all class start here will be replicated in all works.
asWorker = new ASWorker(this.stage, uiWorkerStartedHandler, backWorkerStartedHandler);
}
//BOTH AREA END
//UI AREA START
private function uiWorkerStartedHandler():void{
//implement all class or calls for your UI
NotificationCenter.addEventListener("FROM_BACK_EVENT_MESSAGE", onFromBackEventMessageHandler );
}
private function onFromBackEventMessageHandler(e:NotificationCenterEvent):void
{
trace(e.data);
if(e.data == "completed job"){
NotificationCenter.dispatchEventBetweenWorkers( new NotificationCenterEvent("NEXT_MESSAGE") );
}
}
//UI AREA END
//BACK AREA START
private function backWorkerStartedHandler():void{
//implement all class or calls for your BACK operations
NotificationCenter.addEventListener("NEXT_MESSAGE", uiCallForNextMessageHandler );
}
private function uiCallForNextMessageHandler():void
{
for(var i:int = 0; i < 15; ++i){
NotificationCenter.dispatchEventBetweenWorkers( new NotificationCenterEvent("FROM_BACK_EVENT_MESSAGE", false, i) );
}
NotificationCenter.dispatchEventBetweenWorkers( new NotificationCenterEvent("FROM_BACK_EVENT_MESSAGE", false, "completed job") );
}
// BACK AREA END
}
}
Good luck with Workers
Air 20.0.0.204 ADL is also not enjoying anything worker related (from my limited experimentation), however the desktop Flash Player seems to work just fine. This makes debugging convoluted and problematic when you are using Air specific libraries in your project and prefer not to wildcard variable types.
At the moment, workers are only supported on desktop platform. This Adobe developer's guide page specifically mentions "for desktop platforms".
Meanwhile, there is an AS3-Worker-Compat library by Jeff Ward that enables you to write code with Workers that will still work correctly in an environment that doesn't support Workers (of course, in this case your code will run in single-threaded mode).
I think many people are waiting for Adobe to implement workers for mobile, hopefully it will come in the future.

AS3 SecurityPanel not showing when hardware acceleration is used

If I publish Windows Projector (.exe) with no hardware acceleration, everything works fine, but if I select direct or gpu harware acceleration I will not get the security panel to show up at all. Anyone else have this same issue, or is it just me?
Basically I would like to publish a stand-alone webcam game, but if I try to use getCamera(), the user will not see the security question at all, and I will not get the camera initialized.Does anyone know a workaround for this? I would love to get HW acceleration to my game.
import flash.system.Security;
import flash.system.SecurityPanel;
//import flash.media.Camera;
Security.showSettings(SecurityPanel.CAMERA);
//var camera:Camera=Camera.getCamera();
I got this to work, but I created a projector through the stand alone and the "Create Projector" menu, not through the Flash IDE. I was able to get the panel to show up using the latest flash.
compiler settings: -swf-version=16 -use-gpu=true
// test showing stage3D + panel
final public class Test extends Sprite {
private var cam:Camera;
private var video:Video = new Video();
public function Test():void {
var stage3D:Stage3D = stage.stage3Ds[0];
stage3D.addEventListener(Event.CONTEXT3D_CREATE, handleContext);
stage3D.requestContext3D();
trace(cam.name);
}
/**
* #private
*/
private function handleContext(e:Event):void {
var stage3D:Stage3D = stage.stage3Ds[0];
var context:Context3D = stage3D.context3D;
context.configureBackBuffer(stage.stageWidth, stage.stageHeight, 0, true);
context.clear(0,0,0,1);
context.present();
Security.showSettings(SecurityPanel.CAMERA);
cam = Camera.getCamera();
cam.setMode(640,360,30);
video.attachCamera(cam);
// change to upload via texture instead
addChild(video);
}
In my experience, hardware acceleration gains are only very minimal without using specific gpu apis. Camera in my experience instead accelerated, only specific types of video decompression. If you're not using Stage3D, you probably won't get any visible gains.

Managing Singletons in external swfs

I'm dealing with the scenario whereby my code might be included in other Flash content either included via import .as commands and then referenced as a Singleton, e.g.
import com.as3.Singleton;
...
...
Singleton.birth();
Singleton.getInstance().test();
...but also imported as a runtime library; with the Singleton class exported as a .swf beforehand (instead of pre-baking the class).
How should I reference the Singleton once Event.COMPLETE has fired off from the Loader that brings in the swf? Normally I'd code something like:
public function singletonCompleteHandler(event:Event):void {
var mySing:Singleton = _loader.contentLoaderInfo.content as Singleton;
}
...but I know I don't want to be referencing the singleton via a "var" reference. I'm not explaining very well, but basically once the singleton.swf has loaded in I need to use the code within it within a singleton model (i.e. ensure there's only one instance of it throughout my application).
Copy of the Singleton class included below (thanks for any thoughts on this by the way).
package
{
public class Singleton extends Sprite
{
private static var instance:Singleton;
public function Singleton() {
if (instance) {
throw new Error("Singleton can only be accessed through Singleton.getInstance()");
}
}
public static function birth() {
if (instance == null) {
instance = new Singleton();
}
}
public static function getInstance():Singleton {
return instance;
}
public function test():void {
trace("Testing our singleton.");
}
}
}
First of all, if you're loading it dynamically, then you don't want to have a reference to it in your loading SWF (otherwise it would defeat the point).
So I'm guessing you're looking to do something like this:
function completeHandler(event:Event):void
{
var singleton:Object = loader.contentLoaderInfo.content;
var instance:IMyObject = singleton.getInstance();
instance.test();
}
IMyObject is of course optional here. If you do it like this, your singleton instance will have to implement IMyObject.
interface IMyObject
{
function test():void;
}
This is all to avoid having to reference the actual class in your loading SWF. Like I said, the interface is optional: you can just use Object instead.
... and now on to the main point: load the singleton SWF into the loading SWF's own "application domain".
http://livedocs.adobe.com/flash/9.0/ActionScriptLangRefV3/flash/system/LoaderContext.html#applicationDomain
var lc:LoaderContext = new LoaderContext();
lc.applicationDomain = ApplicationDomain.currentDomain;
loader.load(new URLRequest("Singleton.swf"), lc);
You see, normally when you load a SWF, it gets loaded into its own application domain. But this makes it impossible to enforce the singleton pattern on the loaded SWF, because each instance of the class can live in its own application domain (hence you can end up with multiple instances). So if you want to enforce this across multiple SWF loads then you want to load it into the loading SWF's application domain.
If your question is "How should I reference the Singleton once Event.COMPLETE has fired off from the Loader that brings in the swf?", then you can do it with:
var Singleton:Object = _loader.contentLoaderInfo.applicationDomain.getDefinition('Singleton');
But, I'm not sure what you mean about not wanting to use a "var" reference.
On a side-note, there's a good chance a global variable would be a better option than a Singleton class for an API.
package myPackage
{
public var myGlobal:MyGlobal = new MyGlobal();
}
Which you can access with myPackage.myGlobal

Selecting Input TextField throws Security sandbox violation in loaded swf in Adobe AIR

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

AAC/MP4 not working in ActionScript 3's NetStream

I'm trying to play a remote AAC file in ActionScript 3 in Flash CS3 and am currently using this code:
var url:String = "http://a1.phobos.apple.com/us/r1000/020/Music/d4/50/94/mzm.kjjofihr.aac.p.m4a";
var connect_nc:NetConnection = new NetConnection();
connect_nc.connect(null);
var stream_ns:NetStream = new NetStream(connect_nc);
stream_ns.play(url);
(This is based on: http://www.adobe.com/devnet/flashplayer/articles/hd_video_flash_player_03.html)
No errors are thrown, but no sound is played. I get the same behavior with a local AAC file and with a local MP4 video.
If I use a URL or file path that isn't a streamable file, I get a NetStream.Play.StreamNotFound error, which I'm guessing means that the stream is found in the case of a valid URL. If I use a local FLV, its audio is played just fine.
If I add the following listener and trace(evt.info.code) in netStatusHandler, I only see any codes (e.g. NetStream.Play.Start) traced with the FLV. No codes are traced with the AAC or MP4.
stream_ns.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
The same is true of adding this listener (i.e. the onMetaData argument is only traced with the FLV, not with the other file types), with metaDataListener defined as an object with an onMetaData method that traces its argument.
stream_ns.client = metaDataListener;
Any ideas of what might be going wrong here, or how to diagnose it?
Thanks!
As stated here http://www.adobe.com/devnet/flashplayer/articles/hd_video_flash_player_03.html what you are doing is correct.
var connect_nc:NetConnection = new NetConnection();
connect_nc.connect(null);
var stream_ns:NetStream = new NetStream(connect_nc);
stream_ns.play("RE-Sample.m4a");
However, the Actionscript Language reference about nestream found here:
http://www.adobe.com/livedocs/flash/9.0/ActionScriptLangRefV3/flash/net/NetStream.html#play%28%29
states that:
play () method
...
...
When you use this method without Flash Media Server, there are security considerations. A file in the local-trusted or local-with-networking sandbox can load and play a video file from the remote sandbox, but cannot access the remote file's data without explicit permission in the form of a cross-domain policy file. Also, you can prevent a SWF file running in Flash Player from using this method by setting the allowNetworking parameter of the the object and embed tags in the HTML page that contains the SWF content.
...
...
Parameters
... arguments — The location of the video file to play, as a URLRequest object or a string. In Flash Player and in AIR content outside of the application security sandbox, you can play local video files that are stored in the same directory as the SWF file or in a subdirectory; however, you can't navigate to a higher-level directory.
So it's probably a security sandbox issue.
Everything in ActionScript 3.0 is event based (with few random exceptions where callbacks are used).
You need to listen for the NetStatusEvent with info.code "NetConnection.Connect.Success" in order to be allowed to call the NetStream.play() function.
Here's something that works (I just wrote it now, and tested it for you):
package
{
import flash.display.Sprite;
import flash.net.NetConnection;
import flash.net.NetStream;
import flash.events.NetStatusEvent;
import flash.events.AsyncErrorEvent;
import flash.events.Event;
public class MainDocument extends Sprite
{
private var _connection:NetConnection=new NetConnection();
private var _netStream:NetStream=null;
private var _strM4AURL:String="http://a1.phobos.apple.com/us/r1000/020/Music/d4/50/94/mzm.kjjofihr.aac.p.m4a";
//constructor
public function MainDocument():void
{
this._connect();
}
private function _connect():void
{
this._connection.close();
this._connection=new NetConnection();
this._connection.addEventListener(NetStatusEvent.NET_STATUS, this._netStatusHandler);
this._connection.addEventListener(AsyncErrorEvent.ASYNC_ERROR, this._asyncErrorHandler);
this._connection.connect(null);
}
private function _netStatusHandler(event:NetStatusEvent):void
{
trace(event.info.code);
switch (event.info.code)
{
case "NetConnection.Connect.Success":
this._requestAudio();
break;
}
}
private function _requestAudio():void
{
if(this._netStream!==null)
this._netStream.close();
this._netStream=new NetStream(this._connection);
this._netStream.addEventListener(NetStatusEvent.NET_STATUS, this._netStatusHandler);
this._netStream.addEventListener(AsyncErrorEvent.ASYNC_ERROR, this._asyncErrorHandler);
this._netStream.checkPolicyFile=false;
this._netStream.play(this._strM4AURL);
}
private function _asyncErrorHandler(event:AsyncErrorEvent):void
{
trace(event);
}
}
}
Consult the ActionScript 3.0 Language Reference for more information.
There's a good chance what Oliver is saying is true as your not getting any feedback from any event listeners associated with the NetStream, and you are getting a StreamNotFound response.
StreamNotFound when not connecting to a FMS means that you either have the path wrong or its not seeing it, due to a security issue.